I'm using Symfony2, version 2.7. But anyone should be able to answer this because it's not entirely relevant to symfony.
I have a function. I want that function to add a new item (once clicked on from a form) to an array. Instead, my array keeps getting overwritten with the new item that was clicked.
I tried a few foreach loops but couldn't get it right. Please, any help is appreciated.
Below is relevant function.
/**
* Displays Bought Items
*
* #Route("/buy/{id}", name="item_buy")
* #Method("GET")
* #Template()
*/
public function buyAction(Request $request, $id)
{
$session = $request->getSession();
$cart[] = $id;
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AppBundle:Item')->find($id);
$entities = $em->getRepository('AppBundle:Item')->findAll();
$session->set('cart', $cart);
if (!$entity) {
throw $this->createNotFoundException('No Item ');
} else {
$cart[$entity->getId()] = $entity->getName();
}
$session->set('cart', $cart);
return array(
'cart' => $cart,
'entity' => $entity,
'entities' => $entities,
);
}
How it is being used in twig:
{% extends '::base.html.twig' %}
{% block body -%}
<h1>Items Bought...</h1>
<table class="record_properties">
<h3>You Bought...</h3>
{# {% if entity is defined %} #}
{% for key, cartValue in cart %}
<tr>
<td>{{ key }}: {{ cartValue }}</td>
{{ dump(entity.name) }}
{{ dump(cart) }}
</tr>
{% endfor %}
{# {% endif %} #}
<tbody>
{% for entity in entities %}
<tr>
<td>{{ entity.id }}</td>
<td>{{ entity.name }}</td>
<td>
<ul>
<li>
Buy
</li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<ul class="record_actions">
<li>
<a href="{{ path('item') }}">
Back to the list
</a>
</li>
{% endblock %}
Maybe I am wrong, but I guess that this line is problem:
$cart[] = $id;
You initialize here new array, every time. If I am wright You shuld get this array from session.
Try
$cart = $this->get('session')->get('cart', []);
For better formats of code I am answering to Your last comment here.
Above You're writting about adding new elements and I helped You solve this problem. Now (if I understand correclty) You have problem with added elements to array which You saved in session. I'm surprised your surprise. If You want delete something now from array which You saved in session YOU HAVE TO IMPLEMENT IT. It's not difficult - to clear a cart You should write something like this:
public function clearCartAction(Request $request)
{
$session->set('cart', array()); //set empty array
//return something here..
}
To delete single object from cart something like this (very simple implementation):
public function removeAction(Request $request, $id)
{
$session = $request->getSession();
$em = $this->getDoctrine()->getManager();
$cart = $session->get('cart', array());
if (isset($cart[$id]) {
unset($cart[$id]);
}
$session->set('cart', $cart);
//return something here
}
I can see that You have many very basic problems - definatelly You need a lot of learn and study programming.
Btw. I guess that I helped You solve problem describing in topic - so You should mark my comment as helpfull and topic as resolved.
I have menu items, and menu categories. Categories are assigned to menu items. So a 'pizza' goes under 'dinner' for example.
In my list of menu items, im tring to show the category in the list view but i can't work out how to show the category name in my menu item list as a loop. I have no problem managing this data outside of the context of a list.
This is the index() in my menu items controller
public function index() {
$lists = Menu::orderBy('position')->orderBy('page_name')->paginate( 20 );
return view( 'auth.admin.menu.index' )
->with( 'title', "Menu" )
->with( 'lists', $lists );
}
Loop is . . .
#foreach ($lists as $list)
…
#endforeach
Models are
public function menucats() {
return $this->belongsToMany( 'app\Menucat' )->withTimestamps();
}
public function menu() {
return $this->belongsToMany( 'app\Menu' )->withTimestamps();
}
So the end result of what i am trying to achieve should look like this:
<table>
<thead>
<tr>
<th>id</th>
<th>Menu Items (Menu)</th>
<th>Category (Menucat)</th>
</tr>
</thead>
<tbody>
<tr>
<td>29</td>
<td>Pizza</td>
<td>Dinner</td>
</tr>
<tr>
<td>28</td>
<td>Ice Cream</td>
<td>Desert</td>
</tr>
<tr>
<td>27</td>
<td>Coffee</td>
<td>Hot Drinks</td>
</tr>
</tbody>
</table>
Updated per comment
After re-reading your question I don't believe you need to update your model. If the relationship you want is that many menucats can belong to many menus.
In your controller you would do
public function index() {
$lists = Menu::with('menucats')
->orderBy('position')
->orderBy('page_name')
->paginate( 20 );
return view( 'auth.admin.menu.index' )
->with( 'title', "Menu" )
->with( 'lists', $lists );
}
Then during your loop it would be accessed like this.
#foreach($lists as $list)
...
#foreach($list->menucats as $category)
{{ $category->name }}
#endforeach
...
#endforeach
Updated again per comment
In order to group by menucats, I would get all menucats with their associated menus.
Something like this in your controller should work. The paginate might be tricky.
public function index() {
$lists = Menucats::with(['menu' => function($q){
$q->orderBy('position')
->orderBy('page_name');
])->paginate( 20 );
return view( 'auth.admin.menu.index' )
->with( 'title', "Menu" )
->with( 'lists', $lists );
}
Check out Constraining Eager Loads under Querying Relations
First I've read documents for both Collection Field Type and How to Embed a Collection of Forms ... The example is about one entity (Task) that has one-to-many relation with another entity (Tag), and I understand it, but I can not adapt it to what I want!
To make it simpler, let say I have a Task entity, this task entity has some relations with other objects like user and project (each task can have one user and one project)
I want to make one form, inside this form a list of Tasks, each task in one row of a table that shows information like task.title, task.startdate, task.user.name, task.user.company.name, task.project.name, And it has 2 fields editable, textbox "Description" and checkbox "active". You can edit multiple tasks and submit the form using one button at the bottom of the table in the main form, so basically you should be able to update multiple records in one transaction (instead of making one form and one submit button per row and therefor one record update per submit).
I have many issues with this complicated design:
First I wanted to follow the sample to embed a collection of forms inside the main form, So I made a Form Type for my Task that should be like one form per row. I made these files:
Form Type for Task:
// src/Acme/TaskBundle/Form/Type/TaskType.php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('description', 'text', ['label' => false, 'required' => false, 'attr' => ['placeholder' => 'description']]);
$builder->add('active', 'checkbox', ['label' => false, 'required' => false, 'data' => true]);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\TaskBundle\Entity\Task',
));
}
public function getName()
{
return 'taskType';
}
}
Form Type for main form:
// src/Acme/TaskBundle/Form/Type/SaveTasksType.php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Acme\TaskBundle\Form\Type\TaskType.php;
class SaveTasksType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('tasksCollection', 'collection', ['type' => new TaskType()]);
$builder->add('tasksSubmit', 'submit', ['label' => 'Save']);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'attr' => ['class' => 'form-horizontal'],
'method' => 'POST'
]);
}
public function getName()
{
return 'saveTasksType';
}
}
Tasks Form Controller:
// src/Acme/TaskBundle/Controller/ManageTasksController.php
namespace Acme\TaskBundle\Controller;
use Acme\TaskBundle\Entity\Task;
use Acme\TaskBundle\Form\Type\SaveTaskType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ManageTasksController extends Controller
{
public function showListAction(Request $request)
{
$repository = $this->getDoctrine()->getRepository('ExampleBundle:Task');
$tasks = $repository->findAll();
$taskSaveForm = $this->createForm(new SaveTasksType(['tasks' => $tasks]));
return $this->render('AcmeTaskBundle:Task:list.html.twig', array(
'taskSaveForm' => $taskSaveForm->createView(),
));
}
}
Task Form Twig Template (just related part):
<div class="innerAll">
{{ form_start(taskSaveForm) }}
{{ form_errors(taskSaveForm) }}
<table class="table table-bordered table-striped table-primary list-table">
<thead>
<tr>
<th>Task ID</th>
<th>Title</th>
<th>Start Date</th>
<th>User</th>
<th>Company</th>
<th>Project</th>
<th>Description</th>
<th>Active</th>
</tr>
</thead>
<tbody>
{% for task in taskSaveForm.tasksCollection %}
<tr>
<td>{{ task.id }}</td>
<td>{{ task.title }}</td>
<td>{{ task.startDate }}</td>
<td>{{ task.userName }}</td>
<td>{{ task.companyName }}</td>
<td>{{ task.projectName }}</td>
<td>{{ form_widget(task.description) }}</td>
<td>{{ form_widget(task.active) }}</td>
<td></td>
</tr>
{% endfor %}
</tbody>
</table>
<div>{{ form_row(taskSaveForm.tasksSubmit) }}</div>
{{ form_end(taskSaveForm) }}
</div>
BUT there is an issue here, when I get the result from query builder it is a mess of arrays containing objects in them, I get an error about
The form's view data is expected to be an instance of class Acme\TaskBundle\Entity\Task, but is a(n) array. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) array to an instance of Acme\TaskBundle\Entity\Task.
This is the query:
createQueryBuilder()
->select(
"
task.id,
task.title,
task.startDate,
task.description,
user.name as userName,
company.name as companyName,
project.name as projectName,
"
)
->from('Acme\TaskBundle\Entity\Task', 'task')
->innerJoin('task.project', 'project')
->innerJoin('task.user', 'user')
->innerJoin('Acme\TaskBundle\Entity\Company', 'company', 'with', 'store.company = company')
->where('task.active = :isActive')->setParameter('isActive', true);
Soooo, I used Partial Objects guide to see if it can help, it helps to make the task object in the query result and I could extract it and send it to form, but still it seems the rest of form is unaware of the rest of objects...
Ok, so maybe I'm choosing the wrong approach, I'm not sure! please if you have any suggestions about what should I do, put a note here... I'm struggling with this for more than a week! Thanks in advance for your time! Even if you don't put any note, I appreciate that you spend time reading my very long question! Thanks! :)
Here's a start on a possible solution. The example below takes a single entity Skills and presents all of them on a single page. What I don't know is whether this technique can be used to persist children objects. I would expect one could loop through the returned data and persist as required.
The code below results in a page with a list of all possible Skills and a checkbox for declaring each enabled or enabled.
In a controller:
$skills = $em->getRepository("TruckeeMatchingBundle:Skill")->getSkills();
$formSkills = $this->createForm(new SkillsType(), array('skills' => $skills));
...
if ($request->getMethod() == 'POST') {
$formSkills->handleRequest($request);
foreach ($skills as $existingSkill) {
$em->persist($existingSkill);
}
}
...
return ['formSkills' => $formSkills->createView(),...]
In a template:
{% for skill in formSkills.skills %}
{{ skill.vars.value.skill }}
<input type="hidden" name="skills[skills][{{ loop.index0 }}][skill]" value="{{ skill.vars.value.skill }}">
<input type="checkbox" name="skills[skills][{{ loop.index0 }}][enabled]"
{%if skill.vars.value.enabled %}checked="checked"{%endif%}
{% endfor %}
I use a different strategy. My TWIG file is similar to that of Monica's question. But has a few but very useful differences. Here your code:
{{ form_start(form) }}
{% for docente in docentes %}
Id: <input type="integer" name="{{ docente.id }}" required="required" style="width:30px" value="{{ docente.id }}" readonly>
Apellido: <input type="text" name="{{ docente.apellido }}" required="required" style="width: 80px" value="{{ docente.apellido }}" readonly>
Nombres: <input type="text" name="{{ docente.nombres }}" required="required" style="width: 80px" value="{{ docente.nombres }}" readonly>
Discrecional: <input type="checkbox" name="D{{ docente.id }}" value="{{ docente.discrecional }}" {% if docente.discrecional==1 %}checked{% endif %}> <br>
<br>
{% endfor %}
<input type="submit" value="Grabar" />
{{ form_end(form) }}
In TWIG file, I proceed to create a different name for each record in the form for the "discrecional" field. This will help me to identify each record in the controller. Watch up.
Once the user presses the "submit" button I perform an iteration in my Controller file, as shown below:
if ($form->isSubmitted() && $form->isValid()) {
$i=0;
foreach ($defaultData as $value) {
$data2= array('id' =>$request->request->get($defaultData[$i]['id']),
'discrecional' =>$request->request->get('D'.$defaultData[$i]['id']));
if (($request->request->get('D'.$defaultData[$i]['id'])== '0' and $defaultData[$i]['discrecional']=='0') or
($request->request->get('D'.$defaultData[$i]['id'])== NULL and $defaultData[$i]['discrecional']=='1'))
{
$em->getRepository('BackendBundle:Docentes')->findDocenteFiltId2($data2);
}
$i=$i+1;
}
But the update of registers, is a work that is done in my Repository file through a query using UPDATE, instead of doing it in the Controller file. To avoid unnecessary queries and a server overload, I only do an UPDATE of records that have previously changed. In the example, the following lines in my controller check if there has been a change in the record (in my case, I'm just editing a field called "discrecional". If the field has changed, then I call the query and update the record):
if (($request->request->get('D'.$defaultData[$i]['id'])== '0' and $Data[$i]['discrecional']=='0') or
($request->request->get('D'.$defaultData[$i]['id'])== NULL and $defaultData[$i]['discrecional']=='1'))
{
$em->getRepository('BackendBundle:Docentes')->findDocenteFiltId2($data2);
}
My complete Controller file is here:
public function discrecionalAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$defaultData= $em->getRepository('BackendBundle:Docentes')->buscarDocentesActivos2();
// construimos un formulario "vacío" sin campos definido
$form = $this->createFormBuilder($defaultData);
$form = $form->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$i=0;
foreach ($defaultData as $value) {
$data2= array('id' =>$request->request->get($defaultData[$i]['id']),
'discrecional' =>$request->request->get('D'.$defaultData[$i]['id']));
if (($request->request->get('D'.$defaultData[$i]['id'])== '0' and $defaultData[$i]['discrecional']=='0') or
($request->request->get('D'.$defaultData[$i]['id'])== NULL and $defaultData[$i]['discrecional']=='1'))
{
$em->getRepository('BackendBundle:Docentes')->findDocenteFiltId2($data2);
}
$i=$i+1;
}
return $this->redirectToRoute('docentes_discrecional');
}
return $this->render('docentes/discrecional.html.twig', array(
'docentes' =>$defaultData,
'form' => $form->createView() ));
}
My complete first Repository query is here:
public function buscarDocentesActivos2()
{
$fields = array('d.id', 'd.apellido', 'd.nombres', 'd.discrecional');
$query = $this->getEntityManager()->createQueryBuilder();
$query
->select($fields)
->from('BackendBundle:Docentes', 'd')
->where('d.activo=true')
->orderBy('d.apellido, d.nombres');
$consulta = $query->getQuery()->getResult();
return $consulta;
}
My complete final Repository query with the UPDATE function is here:
public function findDocenteFiltId2($filtro)
{
if (is_null($filtro['discrecional'])){
$discrec= '0';
};
if ($filtro['discrecional']=='0'){
$discrec= '1';
};
$em = $this->getEntityManager();
$consulta = $em->createQuery('
UPDATE BackendBundle:Docentes d
SET d.discrecional = :disc
WHERE d.id = :idver
');
$consulta->setParameters(array(
'idver' => $filtro['id'],
'disc' => $discrec,
));
return $consulta->getArrayResult();
}
I am new to Symfony, I have a table users with two columns: user_name and first_name, I want to display all users whose names (user_name & first_name) contains $search:
<?php
namespace Login\LoginBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Login\LoginBundle\Entity\Users;
use Login\LoginBundle\Modals\Login;
use Doctrine\DBAL\DriverManager;
public function homeAction(Request $request) {
$session = $this->getRequest()->getSession();
if ($request->getMethod() == 'POST') {
$search_for = $request->get('search');
//what to write here --save my life guys
}
return $this->render('LoginLoginBundle:Default:home.html.twig');
}
It's like a full process of querying and fetching data, passing the results to the template. What you need to do is following below steps:
1) Get the repository of your entity (table) and call your custom search function which is created in the repository (of course you can merge this step and next one to have all in your controller homeAction function) I assume your entity name is Users
public function homeAction(Request $request) {
$session = $this->getRequest()->getSession();
$results = array();
if ($request->getMethod() == 'POST') {
$search_for = $request->get('search');
$em = $this->getDoctrine()->getManager();
$results = $em->getRepository('LoginLoginBundle:Users')->searchPhrase($search_for);
}
return $this->render('LoginLoginBundle:Default:home.html.twig', array(
'results' => $results
));
}
2) Now you need to build a repository class for you entity and implement below function in it Entity Repository in Symfony (or merge the code below to your controller)
public function searchPhrase($phrase) {
$query = $this->createQueryBuilder('U');
$result = $query->where(
$query->expr()->like('U.username', $query->expr()->literal("%$phrase%"))
)
->andWhere(
$query->expr()->like('U.firstName', $query->expr()->literal("%$phrase%"))
)
->getQuery()
->getResult();
return $results;
}
Keep in mind because you mention "AND" I used andWhere whereas you can use orWhere; because you said "CONTAIN" I used like expression with % and that's the way you can create expression in DQL Here is a full list of Doctrine2 expressions
3) last step is to show the results in your twig template; I assume you want to show them in table rows
<table>
<tr>
<th>Username</th><th>First Name</th>
</tr>
{% if results|length > 0%}
{% for item in results %}
<tr>
<td>{{ item.getUsername }}</td><td>{{ item.getFirstName }}</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="2">No matching results found!</td>
</tr>
{% endif %}
</table>
Hopefully this helps
Thank you guys, But I this worked for me:
$repository = $this->getDoctrine()->getRepository('LoginLoginBundle:Users');
$query = $repository->createQueryBuilder('u')
->where("u.userName LIKE '%".$search_for."%' or u.firstName LIKE '%".$search_for."%'")
->getQuery();
$Myusers = $query->getResult();
if ($Myusers) //test if there are results returned
{
foreach ($Myusers as $user)
{
echo "E-mail : ".$user->getUserName()." -- Name : ". $user->getFirstName()."<br>";
}
}
else
{
echo "No matching results found!!";
}
//end-display
I made a web-application with symfony2, where a registrated user can query for a file and visualize it. I'm trying to pass the information from the controller to the template.
When I pass the information of a single object, it works properly. You can see the controller here:
public function showAction($id)
{
$product = $this->getDoctrine()
->getRepository('AcmeBundle:Product')
->find($id);
if (!$product) {
throw $this->createNotFoundException(
'Nessun prodotto trovato per l\'id '.$id
);
}
return $this->render('AcmeGroundStationBundle::showdata.html.twig', array('Id' => $product->getId(), 'Name' => $product->getName(), 'UploadTime'=> $product- >getUploadTime()));
}
But what can I do if I want to display the whole list?
If I change the
->find($id);
with
->findAll();
of course I get error.
( Call to a member function getId() on a non-object).
How can I display the whole list?
Thank you for your help
First pass all the products data to your twig view
public function showAction()
{
$products = $this->getDoctrine()
->getRepository('AcmeBundle:Product')
->findAll();
if (empty($products)) {
throw $this->createNotFoundException(
'No products found'
);
}
return $this->render('AcmeGroundStationBundle::showdata.html.twig',
array('products' => $products));
}
Then in your view you can list products with their information as
{% if products is defined and products is not empty %}
{% for p in products %}
id : {{ p.getId() }} <br>
Name: {{ p.getName() }} <br>
Upload Time: {{ p.getUploadTime() }} <br>
{% endfor %}
{% endif %}
EDIT
findAll() will give all the results to get the latest 10 you need use findBy
findBy(
array(), // $where
array('id' => 'DESC'), // $orderBy
10, // $limit
0 // $offset
);