How to loop an array collection to display in strings - twig

I want to display in Twig a string list of "strenghts" for a "strategy" that I get in the controller with ParamConverter (there is a one to many relation, and "strenghts" is an attribute in strategy as an array collection).
I could make it work in the controller to get the right data in my twig file (verified with a dump), but nothing is displayed in Twig (and yes data is written in database, and I checked it's not a front problem).
So I have read many topics about the same problem, and a lot of them say that I have to loop on the array collection to be able to display it in strings, which I tried but couldn't make it work.
Controller file :
/**
* #Route("fr/strategy/content/{title}", name="frContentStrategy")
*/
public function displayStrategy(Strategy $strategy): Response
{
$paramStrategy = $this->getDoctrine()->getRepository(Strategy::class)->find($strategy);
return $this->render('content_strategy/contentStrategy.html.twig', [
"strategy" => $strategy,
"paramStrategy" => $paramStrategy
]);
}
Twig file :
{% for paramStrategy in paramStrategy %}
{{ paramStrategy.strenghts }}
{% endfor %}
{{ dump(paramStrategy.strenghts)}}
What my dump displays :
I also tried a loop inside a loop like this but I get the same result with nothing displayed, and the same data with my dump :
{% for paramStrategy in paramStrategy %}
{{ paramStrategy.strenghts }}
{% for strategy in paramStrategy.strenghts %}
{{ strategy.strenghts }}
{% endfor %}
{% endfor %}
{{ dump(strategy.strenghts)}}
Edit, here are my two entities:
Strategy :
class Strategy
{
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="strategies")
*/
private $user;
/**
* #ORM\OneToMany(targetEntity=DiagnosticForce::class, mappedBy="strategy")
*/
private $strenghts;
public function __construct()
{
$this->strenghts = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
/**
* #return Collection|DiagnosticForce[]
*/
public function getStrenghts(): Collection
{
return $this->strenghts;
}
public function addStrenght(DiagnosticForce $strenght): self
{
if (!$this->strenghts->contains($strenght)) {
$this->strenghts[] = $strenght;
$strenght->setStrategy($this);
}
return $this;
}
public function removeStrenght(DiagnosticForce $strenght): self
{
if ($this->strenghts->removeElement($strenght)) {
// set the owning side to null (unless already changed)
if ($strenght->getStrategy() === $this) {
$strenght->setStrategy(null);
}
}
return $this;
}
public function __toString()
{
return $this->title;
}
}
DiagnosticForce :
class DiagnosticForce
{
private $id;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $strenght;
/**
* #ORM\ManyToOne(targetEntity=Strategy::class, inversedBy="strenghts")
*/
private $strategy;
public function getId(): ?int
{
return $this->id;
}
public function getStrenght(): ?string
{
return $this->strenght;
}
public function setStrenght(?string $strenght): self
{
$this->strenght = $strenght;
return $this;
}
public function getStrategy(): ?Strategy
{
return $this->strategy;
}
public function setStrategy(?Strategy $strategy): self
{
$this->strategy = $strategy;
return $this;
}
}

With autowiring your $strategy parameter should already be the same entity as $paramStrategy, no need to use the repository.
So, in twig you should just need the following:
{% for strenght in strategy.strenghts %}
{{ strenght }}
{% endfor %}
This should have the same result as the following in php:
foreach ($strategy->getStrenghts() as $strenght){
echo "\n$strenght\n";
}

Related

How to access saved entity via metadata using parameters in the route

I have an entity contact
<?php
namespace App\Entity;
use App\Repository\ContactRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ContactRepository::class)
*/
class Contact
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=50)
*/
private $firstName;
/**
* #ORM\Column(type="string", length=50)
*/
private $lastName;
/**
* #ORM\Column(type="date", nullable=true)
*/
private $dob;
/**
* #ORM\Column(type="string", length=50)
*/
private $email;
/**
* #ORM\Column(type="string", length=15)
*/
private $phone;
public function getId(): ?int
{
return $this->id;
}
public function getFirstName(): ?string
{
return $this->firstName;
}
public function setFirstName(string $firstName): self
{
$this->firstName = $firstName;
return $this;
}
public function getLastName(): ?string
{
return $this->lastName;
}
public function setLastName(string $lastName): self
{
$this->lastName = $lastName;
return $this;
}
public function getDob(): ?\DateTimeInterface
{
return $this->dob;
}
public function setDob(?\DateTimeInterface $dob): self
{
$this->dob = $dob;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getPhone(): ?string
{
return $this->phone;
}
public function setPhone(string $phone): self
{
$this->phone = $phone;
return $this;
}
}
I want to select one (or more) contact with this URL: /c00210/readallbyparam/{routeProperty}/{routeValue}
For example: /c00210/readallbyparam/lastname/Mendelsohn
This should return 2 records:
Felix Mendelsohn
Fanny Mendelsohn
How do I write the render() function?
How do I write the twig instructions?
I suppose I should use the metadata, but I don't know
+++++++++++++++++++++++++++++++++++++++
here is the function in my controller
/**
* #Route("/c00210/readallbyparam/{routeProperty}/{routeValue}", name="c00210_read_all_by_param")
*/
public function findAllByparam( $routeProperty,$routeValue)
{
$property = $routeProperty;
$value = $routeValue;
$repository = $this->getDoctrine()->getRepository(Contact::class);
$contacts = $repository->findAll();
return $this->render('c00210/readbycriteria2.html.twig', [
'function' => 'find(<id>)',
'description' => 'finds the with the given Id',
'property' => $property,
'value' => $value,
'contacts' => $contacts,
]);
routeProperty should be a column name, and routeValue a column value
shoud be equivalent to
SELECT * FROM contacts WHERE lastname = 'Mendelsohn'

How to build Excel with two sheets and download?

I use Laravel Excel and that is full code:
<?php
namespace App\Http\Controllers;
use App\Event;
use App\Visitor;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Facades\Excel;
use Exportable;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
class VisitorsSheet implements FromQuery, WithTitle
{
private $idEvent;
public function __construct($idEvent)
{
$this->idEvent = $idEvent;
}
public function query()
{
return Visitor::where("idEvent", $this->idEvent)->get();
}
public function title(): string
{
return 'Visitors';
}
}
class EventSheet implements FromQuery, WithTitle
{
private $idEvent;
public function __construct($idEvent)
{
$this->idEvent = $idEvent;
}
public function query()
{
return Event::where("idEvent", $this->idEvent)->get();
}
public function title(): string
{
return 'Event №' . $this->idEvent;
}
}
class ArchivingExport implements WithMultipleSheets
{
private $eventId;
public function __construct($eventId)
{
$this->eventId = $eventId;
}
public function sheets(): array
{
$sheets = [];
$sheets[] = new EventSheet($this->eventId);
$sheets[] = new VisitorsSheet($this->eventId);
return $sheets;
}
}
class ArchivingController extends Controller
{
private $file = "settings_archive.json";
public function __construct()
{
date_default_timezone_set("Asia/Tei");
}
private function formatName($event)
{
return $event->date . '_' . $event->name . '.xlsx';
}
public function index()
{
$download = [];
try {
$events = Event::where("status", 1)->where("archived", 0)->get();
foreach ($events as $key => $event) {
$download[] = new ArchivingExport($event->idEvent); //->download($this->formatName($event));
}
return $download;
} catch (Exception $e) {
return \json_decode([
'status' => false,
'message' => $e->getMessage()
]);
}
return \json_decode([
'status' => false
]);
}
}
As you can see in class ArchivingController in index method I try to build sheets:
foreach ($events as $key => $event) {
$download[] = new ArchivingExport($event->idEvent); //->download($this->formatName($event));
}
I get ready objects in $download[], but I dont know how to download them? It should be ready Excel file.
I tried to call for each object:
->download($this->formatName($event));
But there is not method download inside
It is not possible to send more than one file simultaneously over the same request with the HTTP protocol. Laravel also does not support this.
You have to pack the files in, for example, a zip file.
A popular package is Zipper

Symfony forms - Expected argument of type , "string" given at property path

I get this error message:
Expected argument of type "App\Entity\Artist or null", "string" given at property path "artist".
This is the code of my form in symfony
$builder
->add('date', DateType::class, [
'widget' => 'single_text'
])
->add('artist', TextType::class)
->add('City',TextType::class)
;
And this is the template:
{{ form_start(form)}}
{{ form_widget(form)}}
<button class="btn btn-secondary">{{ button|default('Enregistrer')}}</button>
{{ form_end(form)}}
Entity
class Artist
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=45)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Event", mappedBy="artist", cascade={"persist","remove"})
*/
private $events;
public function __construct()
{
$this->events = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(?string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|Event[]
*/
public function getEvents(): Collection
{
return $this->events;
}
public function addEvent(Event $event): self
{
if (!$this->events->contains($event)) {
$this->events[] = $event;
$event->setArtist($this);
}
return $this;
}
public function removeEvent(Event $event): self
{
if ($this->events->contains($event)) {
$this->events->removeElement($event);
// set the owning side to null (unless already changed)
if ($event->getArtist() === $this) {
$event->setArtist(null);
}
}
return $this;
}
public function __toString()
{
return $this->name;
}
}
This is the error :
Uncaught PHP Exception Symfony\Component\PropertyAccess\Exception\InvalidArgumentException: "Expected argument of type "App\Entity\Artist or null", "string" given at property path "artist"." at D:\wamp64\www\app_music_events\vendor\symfony\property-access\PropertyAccessor.php line 173
{
"exception": {}
}
This is the controller
/**
* Method for editing an event
*
* #Route("/admin/edit/{id}", name="admin.edit", methods="GET|POST")
* #param Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function edit(Event $event, Request $request): Response
{
$form = $this->createForm(EventType::class, $event);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->em->flush();
$this->addFlash('success', 'Le concert a été modifié avec succès !');
return $this->redirectToRoute('admin.index');
}
return $this->render('admin/edit.html.twig', [
'event' => $event,
'form' => $form->createView()
]
);
}
I don't understand.
I do not have this problem when I do not specify Textype in the form.
What is the problem? Thanks
The problem is that you need to persist an Artist entity along with your Event. Symfony expects an Artist entity type and not a string. Try with EntityType::class instead of TextType::class
->add('artist', EntityType::class, [
'class' => Artist::class
]);
For more information on the EntityType field, please check the documentation

SignalR KnockoutJS and AzureTodo OnChangeEvent

I would like CollectionChanged triggered when todo items is inserted in my table for Azure from elsewhere.
I have a mobileapp where i can insert items via TodoItemManager.
I have a website where items is listed.
Now i would like without refreshing the site, items should get added.
One option is to have a setInterval but i dont like that option.
<div class="products">
<div class="row" data-bind="template: { name: 'productTemplate', foreach: products }">
</div>
<span class="messageClass" style="color: red;"></span>
</div>
<script type="text/html" id="productTemplate">
<div class="col-sm-6 col-md-4">
<div class="thumbnail">
<div class="caption">
<h3 data-bind="text: name"></h3>
</div>
</div>
</div>
</script>
<script>
$(function () {
//debugger;
function productViewModel(id, name) {
this.productId = id;
this.name = ko.observable(name);
var self = this;
}
function productListViewModel() {
//debugger;
this.hub = $.connection.myHub;
this.products = ko.observableArray([]);
var products = this.products;
this.init = function () {
this.hub.server.getAllProducts();
}
this.hub.client.getAllProducts = function (allProducts) {
//debugger;
var mappedProducts = $.map(allProducts, function (item) {
//debugger;
return new productViewModel(item.ProductId, item.Name)
});
products(mappedProducts);
}
}
var vm = new productListViewModel();
ko.applyBindings(vm);
$.connection.hub.start(function () {
vm.init();
}).done(function () {
});;
});
</script>
Another option is to trigger method GetAllProducts in my Hub from the app so it will trigger
await Clients.All.getAllProducts(vm.Products.ToArray());
But i dont like that approach.
I would like to know how i can update my data that is listed on my webpage when i add items to azure table without refreshing the browser.
Here is my hub
public class MyHub : Hub
{
private ObservableCollection<TodoItem> persons;
public MyHub()
{
}
public async Task GetAllProducts()
{
var data = await GetLogs();
VoteViewModel vm = new VoteViewModel();
vm.Products = data.Select(m => new Products() { Name = m.Name }).ToList();
//vm.Products = new List<Products>() { new Products() { Name = "Sample", ProductId = 1 } };
await Clients.All.getAllProducts(vm.Products.ToArray());
}
private async Task<List<TodoItem>> GetLogs()
{
persons = await TodoItemManager.DefaultManager.GetTodoItemsAsync();
persons.CollectionChanged += this.OnCollectionChanged;
return persons.ToList();
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
}
class VoteViewModel
{
public List<Products> Products { get; set; }
}
public class Products
{
public int ProductId { get; set; }
public string Name { get; set; }
}
}
And a look in TodoItemManager:
public partial class TodoItemManager
{
static TodoItemManager defaultInstance = new TodoItemManager();
MobileServiceClient client;
#if OFFLINE_SYNC_ENABLED
IMobileServiceSyncTable<TodoItem> todoTable;
#else
IMobileServiceTable<TodoItem> todoTable;
#endif
const string offlineDbPath = #"localstore.db";
private TodoItemManager()
{
this.client = new MobileServiceClient(AzureSettings.ApplicationURL);
#if OFFLINE_SYNC_ENABLED
var store = new MobileServiceSQLiteStore(offlineDbPath);
store.DefineTable<TodoItem>();
//Initializes the SyncContext using the default IMobileServiceSyncHandler.
this.client.SyncContext.InitializeAsync(store);
this.todoTable = client.GetSyncTable<TodoItem>();
#else
this.todoTable = client.GetTable<TodoItem>();
#endif
}
public static TodoItemManager DefaultManager
{
get
{
return defaultInstance;
}
private set
{
defaultInstance = value;
}
}
public MobileServiceClient CurrentClient
{
get { return client; }
}
public bool IsOfflineEnabled
{
get { return todoTable is Microsoft.WindowsAzure.MobileServices.Sync.IMobileServiceSyncTable<TodoItem>; }
}
public async Task<ObservableCollection<TodoItem>> GetTodoItemsAsync(bool syncItems = false)
{
try
{
#if OFFLINE_SYNC_ENABLED
if (syncItems)
{
await this.SyncAsync();
}
#endif
IEnumerable<TodoItem> items = await todoTable
.Where(todoItem => !todoItem.Done)
.ToEnumerableAsync();
return new ObservableCollection<TodoItem>(items);
}
catch (MobileServiceInvalidOperationException msioe)
{
Debug.WriteLine(#"Invalid sync operation: {0}", msioe.Message);
}
catch (Exception e)
{
Debug.WriteLine(#"Sync error: {0}", e.Message);
}
return null;
}
public async Task SaveTaskAsync(TodoItem item)
{
if (item.Id == null)
{
await todoTable.InsertAsync(item);
}
else
{
await todoTable.UpdateAsync(item);
}
}
#if OFFLINE_SYNC_ENABLED
public async Task SyncAsync()
{
ReadOnlyCollection<MobileServiceTableOperationError> syncErrors = null;
try
{
await this.client.SyncContext.PushAsync();
await this.todoTable.PullAsync(
//The first parameter is a query name that is used internally by the client SDK to implement incremental sync.
//Use a different query name for each unique query in your program
"allTodoItems",
this.todoTable.CreateQuery());
}
catch (MobileServicePushFailedException exc)
{
if (exc.PushResult != null)
{
syncErrors = exc.PushResult.Errors;
}
}
// Simple error/conflict handling. A real application would handle the various errors like network conditions,
// server conflicts and others via the IMobileServiceSyncHandler.
if (syncErrors != null)
{
foreach (var error in syncErrors)
{
if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
{
//Update failed, reverting to server's copy.
await error.CancelAndUpdateItemAsync(error.Result);
}
else
{
// Discard local change.
await error.CancelAndDiscardItemAsync();
}
Debug.WriteLine(#"Error executing sync operation. Item: {0} ({1}). Operation discarded.", error.TableName, error.Item["id"]);
}
}
}
#endif
}
As you can se in my hub i tried with
this.OnCollectionChanged
But this will change only if
private ObservableCollection<TodoItem> persons;
changes and this will not happen so i need to invoke
GetTodoItemsAsync
again to fetch the data.
How do i update my list on client when new items is added to azure table without refreshing the whole site?
Hope you understand where im aiming at.
BR
Another option would be to set a interval on server instead of client.
Like:
public class MyHub : Hub
{
private ObservableCollection<TodoItem> persons;
public MyHub()
{
Intervalla();
}
public async Task Intervalla()
{
EasyTimer.SetInterval(async () =>
{
var data = await GetLogs();
VoteViewModel vm = new VoteViewModel();
vm.Products = data.Select(m => new Products() { Name = m.Name }).ToList();
//vm.Products = new List<Products>() { new Products() { Name = "Sample", ProductId = 1 } };
await Clients.All.getAllProducts(vm.Products.ToArray());
}, 1000);
}
Could this be a smart/er option?

Symfony2.2 Login from Database Error: "Bad Credentials"

I have roughly followed Symfony's Security and How to load Security Users from the Database instructions with some small custom configurations but I can't login using the users I have in my database! The in_memory user works fine!
This is my security.yml:
jms_security_extra:
secure_all_services: false
expressions: true
security:
encoders:
NEWS\BlogBundle\Entity\Author: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
providers:
chain_provider:
chain:
providers: [user_db, in_memory]
user_db:
entity: { class: NEWSBlogBundle:Author , property: username }
in_memory:
memory:
users:
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
firewalls:
admin_area:
pattern: ^/admin
http_basic: ~
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: /Login
check_path: /login_check
username_parameter: username
password_parameter: password
logout:
path: /logout
target: /blog
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/newpost, roles: ROLE_USER }
And my Author.php (User) entity (I have removed the function annotations to make it shorter):
<?php
namespace NEWS\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Author
*/
class Author implements \Symfony\Component\Security\Core\User\AdvancedUserInterface, \Serializable
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $username;
/**
* #var string
*/
private $password;
/**
* #var string
*/
private $Name;
/**
* #var string
*/
private $Surname;
/**
* #var string
*/
private $salt;
/**
* #var boolean
*/
private $isActive;
/**
* #var \NEWS\BlogBundle\Entity\Category
*/
private $category;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function setUsername($username)
{
$this->username = $username;
return $this;
}
public function getUsername()
{
return $this->username;
}
public function setPassword($password)
{
$this->password = $password;
return $this;
}
public function getPassword()
{
return $this->password;
}
public function setCategory(\NEWS\BlogBundle\Entity\Category $category = null)
{
$this->category = $category;
return $this;
}
public function getCategory()
{
return $this->category;
}
public function setName($name)
{
$this->Name = $name;
return $this;
}
public function getName()
{
return $this->Name;
}
public function setSurname($surname)
{
$this->Surname = $surname;
return $this;
}
public function getSurname()
{
return $this->Surname;
}
public function __construct()
{
$this->isActive = true;
$this-> salt = sha1(uniqid(null, true));
}
public function setSalt($salt)
{
$this->salt = $salt;
return $this;
}
public function getSalt()
{
return $this->salt;
}
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
public function getIsActive()
{
return $this->isActive;
}
public function isAccountNonExpired()
{
// TODO: Implement isAccountNonExpired() method.
return true;
}
public function isAccountNonLocked()
{
// TODO: Implement isAccountNonLocked() method.
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isEnabled()
{
return $this->isActive;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function eraseCredentials()
{
// TODO: Implement eraseCredentials() method.
}
/**
* #var \Doctrine\Common\Collections\Collection
*/
private $posts;
public function addPost(\NEWS\BlogBundle\Entity\Post $posts)
{
$this->posts[] = $posts;
return $this;
}
public function removePost(\NEWS\BlogBundle\Entity\Post $posts)
{
$this->posts->removeElement($posts);
}
public function getPosts()
{
return $this->posts;
}
public function serialize()
{
return serialize(array(
$this->id,
));
}
public function unserialize($serialized)
{
list (
$this->id,
) = unserialize($serialized);
}
}
This is my Login.html.twig
{# src/NEWS/BlogBundle/Resources/views/Page/Login.html.twig #}
{% extends 'NEWSBlogBundle::layout.html.twig' %}
{% block title %}Login Page{% endblock%}
{% block body %}
<header>
</header>
{% if app.session.hasFlash('blogger-notice') %}
<div class="blogger-notice">
{{ app.session.flash('blogger-notice') }}
</div>
{% endif %}
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path('login_check') }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="password" />
<button type="submit">Login</button>
</form>
{% endblock %}
Login controller:
public function LoginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
// get the login error if there is one
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(
SecurityContext::AUTHENTICATION_ERROR
);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
return $this->render(
'NEWSBlogBundle:Page:Login.html.twig',
array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
)
);
}
and the function which builds my login form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('username', 'text');
$builder->add('password', 'password');
return $builder->getForm();
}
I've been trying to solve this for three days but have not been sucessful! I would really appreciate any help!
I don't know if the registration functions are needed too or not, I'm not including them right now since the question is long enough so far!
P.S: My login route intentionally starts with a capital letter and I am aware that I have removed the '_' from the user/pass parameteres, I did try changing them back to the original form but stil got the same error!
input field should contain underscore
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<input type="password" id="password" name="_password" />
looks funny, but faced it

Resources