How can edit more products entities in same page (no 1 to many).
In My editaction :
$entities=$em->getRepository('MyBundle:Product')->findAll();
$editForm=array();
$deleteForm=array();
foreach ($entities as $product )
{
$editForm [$port->getId()]= $this->createEditForm($product);
$deleteForm[$port->getId()] = $this->createDeleteForm($product->getId());
}
return $this->render('MyBundle:Product:edit.html.twig', array(
'entities' => $entities,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
I have this error :
Error: Call to a member function createView() on a non-object
And how update the edit.thml.twig to show all form update as table with only one submit ?
This is not about Symfony2, but about PHP, you are calling a method onto an array...
Consider something like this :
$entities = $em->getRepository('MyBundle:Product')->findAll();
$editForms = array();
$deleteForms = array();
foreach ($entities as $product)
{
$editForms[$port->getId()] = $this->createEditForm($product)
->createView();
$deleteForms[$port->getId()] = $this->createDeleteForm($product->getId())
->createView();
}
return $this->render('MyBundle:Product:edit.html.twig', array(
'entities' => $entities,
'edit_forms' => $editForms,
'delete_forms' => $deleteForms,
));
With a template like this one :
{% for form in edit_foms %}
{{ form(form) }}
{% endfor %}
{% for form in delete_foms %}
{{ form(form) }}
{% endfor %}
I have fix this :
change the name of form:
public $name;
/**
* #return string
*/
public function getName()
{
return (string)'port_'.$this->name;
}
public function __construct($name=0) {
$this->name=$name;
}
and in my controller
editAction :
$entities = $em->getRepository('InfraProductBundle:InfraPortDdf')->findAll();
foreach ($entities as $port )
{
$editForm [$port->getId()]= $this->createEditForm($port);
$edit_view[$port->getId()]=$editForm[$port->getId()]->createView();
}
return $this->render('......:edit.html.twig', array(
'entities' => $entities,
'edit_form' => $edit_view,
));
in update action
foreach ($array_id as $key=>$id){
if(!is_numeric(str_replace('port_','',$id)))
continue;
$entity = $em->getRepository('InfraProductBundle:InfraPortDdf')->find(str_replace('port_','',$id));
if (!$entity) {
throw $this->createNotFoundException('Unable to find InfraPortDdf entity.');
}
$editForm = $this->createEditForm($entity);
if($editForm->submit($request->request->get($id)))
$em->flush();
}
in my edit.html.twig:
{% for key, edit in edit_form %}
...
{{form_widget(edit.description,{name:'['~key~'][description]', 'attr': { 'name' : '['~key~'][description]' } } ) }}
....
{%endfor%}
Related
I have used CRUD to generate actions for an entity. However, I would like to modify the edit action; for instance, a user select 'inactive' represented by 1 for State then they will be shown an error message otherwise they can proceed to updating that row. Within the entity these are the variables state, paypid, startDate, endDate.
This is what the edit action currently looks like.
public function editAction($Paypid)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('comtwclagripayrollBundle:Payrollperiod')
->findBy(['paypid' => $Paypid, 'state' => 0]);
if (!$entity) {
$this->addFlash(
'notice',
'You cannot edit an inactive Payroll Period'
);
return $this->redirectToRoute('/payrollperiod');
}
$editForm = $this->createEditForm($entity);
$deleteForm = $this->createDeleteForm($Paypid);
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}
And this method in the repository entity.
public function findByPaypidAndActiveState($Paypid)
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM comtwclagripayrollBundle:Payrollperiod
WHERE paypid = :paypid AND state = :state'
)
->setParameter('paypid', $Paypid)
->setParameter('state', 0)
->getResult();
}
Maybe add to view something like this(only for twig template):
{% for type, flash_messages in app.session.flashBag.all %}
{% for flash_message in flash_messages %}
{{ flash_message }}
{% endfor %}
{% endfor %}
I recomended right bechind <body> tag.
I have an entity Playlist that is related to another call Items.
I want to deploy on a twig template id of each of the items.
class Playlist
{
private $items;
public function __construct()
{
$this->items = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addItem(\Publicartel\AppBundle\Entity\PlaylistContent $content)
{
$content->setPlaylist($this);
$this->duration += $content->getDuration();
$this->items->add($content);
return $this;
}
public function removeItem(\Publicartel\AppBundle\Entity\PlaylistContent $content)
{
$this->items->removeElement($content);
$this->duration -= $content->getDuration();
}
public function getItems()
{
return $this->items;
}
}
I want to deploy in the Playlist form the id of the item.
I've tried like so:
{% for content in edit_form.items %}
{{ content.getId() }}
{{ content.id() }}
{% endfor %}
But I get the following error:
Method "getId" for object "Symfony\Component\Form\FormView" does not
exist Method "id" for object "Symfony\Component\Form\FormView" does
not exist
I've added the id attribute to my FormType:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id')
->add('position')
->add('duration')
->add('playlist')
->add('content')
;
}
But I get the following error:
An exception has been thrown during the rendering of a template
("Catchable Fatal Error: Object of class
Symfony\Component\Form\FormView could not be converted to string")
I've also tried:
// Controller
$entity = $em->getRepository('PublicartelAppBundle:Playlist')->find($id);
return $this->render('PublicartelAppBundle:Playlist:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
// Twig
{{ entity.items.id }}
But that too throws an error:
Method "id" for object "Doctrine\ORM\PersistentCollection" does not exist
Thanks!
Solution 1
Last part of your solution is the good one
$entity = $em->getRepository('PublicartelAppBundle:Playlist')->find($id);
return $this->render('PublicartelAppBundle:Playlist:edit.html.twig', array(
'entity' => $entity,
[...]
));
What isn't good is that snippet of code
{{ entity.items.id }}
You have to cycle over all items before print id out (from your error, is pretty clear)
{% for item in entity.items %}
{{ item.id }}
{% endfor %}
Solution 2 (Didn't tested)
You can, of course, access also data from underlying object of a form, without pass it from the controller. So you can change your snippet of code from
from
{% for content in edit_form.items %}
{{ content.getId() }}
{{ content.id() }}
{% endfor %}
to
{% for content in edit_form.vars.data %}
{{ content.id() }}
{% endfor %}
Your error was that you were trying to access FormView (passed from controller) and not entity "binded" to the form
I have Two Action, GetAllPost and newComment
I have a page with many Post and each Post have commentForm
PostController
public function getPostAction () {
return array(
);
}
Twig
{% for post in app.user.posts %}
<p>{{ post.id }} - {{ post.description }} </p>
{{ render(controller("ADVCommentBundle:Comment:newComment" ,{ 'id': post.id,'redirect':'get_post' } )) }}
<hr>
{%endfor%}
CommentController
public function newCommentAction (Request $request, Post $post) {
$em = $this->getEm();
$comment = new Comment();
$form = $this->createForm(new CommentType(), $comment);
$form->handleRequest($request);
if ($form->isValid()) {
try {
$em->beginTransaction();
$comment->setPost($post);
$em->persist($comment);
$em->flush();
$em->commit();
} catch (\Exception $e) {
$em->rollback();
throw $e;
}
}
return array(
'post' => $post,
'form' => $form->createView(),
);
}
TwifFormController
{{ form(form, {'action': path('new_comment',{'id': post.id})})}}
When I insert a new comment I have redirect to new_comment even if my value isn't valid.
How can I redirect to GeTAllPost and show the correct Error or the new Comment?
I tried to use
return $this->redirect($this->generateUrl('get_post',array('error',$form->getErrors())));
and 'error_bubbling' => true,, but each time request a get_post ( GetAllPost ) I do a new render of my Form and I don't see the errors
For Example i'd like to use newCommentAction in several scenario.
For example i GetAllPost for each post, but even in GetSpecificPost, where I Have A specific post, where I Can insert a new comment, but the save ( and the Action ) is the same.
Do I have create a Service ?
UPDATE
After Bonswouar's answer. This is my Code
PostController
/**
* #Route("/",name="get_posts")
* #Template()
*/
public function getPostsAction () {
$comment = new Comment();
return array(
'commentForms' => $this->createCreateForm($comment),
);
}
private function createCreateForm (Comment $entity) {
$em = $this->getEm();
$posts = $em->getRepository('ADVPostBundle:Post')->findAll();
$commentForms = array();
foreach ($posts as $post) {
$form = $this->createForm(new CommentType($post->getId()), $entity);
$commentForms[$post->getId()] = $form->createView();
}
return $commentForms;
}
/**
* #Method({"POST"})
* #Route("/new_comment/{id}",name="new_comment")
* #Template("#ADVPost/Post/getPosts.html.twig")
* #ParamConverter("post", class="ADVPostBundle:Post")
*/
public function newCommentAction (Request $request, Post $post) {
$em = $this->getEm();
$comment = new Comment();
//Sometimes I Have only One Form
$commentForms = $this->createCreateForm($comment);
$form = $this->createForm(new CommentType($post->getId()), $comment);
$form->handleRequest($request);
if ($form->isValid()) {
try {
$em->beginTransaction();
$comment->setPost($post);
$em->persist($comment);
$em->flush();
$em->commit();
} catch (\Exception $e) {
$em->rollback();
throw $e;
}
} else {
$commentForms[$post->getId()] = $form->createView();
}
return array(
'commentForms' => $commentForms,
);
}
And I Don't have any Render.
But, I want to re-use newCommentAction also in Single Post, and i Want to create Only one Form. I don't want use $commentForms = $this->createCreateForm($comment);, because i Want just one form,and I have to change template even. How can I do ?
If I'm not mistaking, your problem is that you're posting on new_comment, which is a "sub action".
You actually don't need this Twig render.
You could just generate all the forms you need in the main Action, with something like this :
foreach ($posts as $post) {
$form = $this->createForm(new CommentType($post->getId()), new Comment());
$form->handleRequest($request);
if ($form->isValid()) {
//...
// Edited : to "empty" the form if submitted & valid. Another option would be to redirect()
$form = $this->createForm(new CommentType($post->getId()), new Comment());
}
$commentForms[$post->getId()] = $form->createView();
}
return array(
'posts' => $posts,
'commentForms' => $commentForms,
);
Not forgetting to set a dynamic Name in your Form class :
class CommentType extends AbstractType
{
public function __construct($id) {
$this->postId = $id;
}
public function getName() {
return 'your_form_name'.$this->postId;
}
//...
}
And then just normally render your forms in your Twig loop. You should get the errors.
{% for post in app.user.posts %}
<p>{{ post.id }} - {{ post.description }} </p>
{{ form(commentForms[post.id]) }}
<hr>
{%endfor%}
If I didn't miss anything that should do the job.
UPDATE :
After seeing your update, this might be the controller you want (sorry if I didn't understand properly or if I did some mistakes):
/**
* #Route("/",name="get_posts")
* #Template()
*/
public function getPostsAction () {
$em = $this->getEm();
$posts = $em->getRepository('ADVPostBundle:Post')->findAll();
$commentForms = array();
foreach ($posts as $post) {
$commentForms[$post->getId()] = $this->createCommentForm($post);
}
return array(
'commentForms' => $commentForms
);
}
private function createCommentForm (Post $post, $request = null) {
$em = $this->getEm();
$form = $this->createForm(new CommentType($post->getId()), new Comment());
if ($request) {
$form->handleRequest($request);
if ($form->isValid()) {
try {
$em->beginTransaction();
$comment->setPost($post);
$em->persist($comment);
$em->flush();
$em->commit();
} catch (\Exception $e) {
$em->rollback();
throw $e;
}
$form = $this->createForm(new CommentType($post->getId()), new Comment());
}
}
return $form;
}
/**
* #Method({"POST"})
* #Route("/new_comment/{id}",name="new_comment")
* #Template("#ADVPost/Post/getPosts.html.twig")
* #ParamConverter("post", class="ADVPostBundle:Post")
*/
public function newCommentAction (Request $request, Post $post) {
return array(
'commentForm' => $this->createCommentForm($post, $request);
);
}
What about using a flash message to set your error messages? http://symfony.com/doc/current/components/http_foundation/sessions.html#flash-messages
EDIT: Modifying based on your comments. In your controller you could do this:
foreach ($form->getErrors() as $error) {
$this->addFlash('error', $post->getId().'|'.$error->getMessage());
}
or
$this->addFlash('error', $post->getId().'|'.(string) $form->getErrors(true, false));
What this will do is allow you to tie the error to the particular post you want, as you are passing it a string like 355|This value is already used. If you need to know the field you could add another delimiter for $error->getPropertyPath() in your flash message, or you could overwrite the error name in the entity itself.
Then in your controller you could parse out the flash messages, and add them to an array that your twig template would check:
$errors = array();
foreach ($this->get('session')->getFlashBag()->get('error', array()) as $error)
{
list($postId, $message) = explode('|', $error);
$errors[$postId][] = $message;
}
return array('errors' => $errors, ...anything else you send to the template)
Now your twig template can check for the existence of errors on that particular form:
{% for post in app.user.posts %}
{% if errors[post.id] is defined %}
<ul class="errors">
{% for error_message in errors[post.id] %}
<li>{{ error_message }}</li>
{% endfor %}
</ul>
{% endif %}
<p>{{ post.id }} - {{ post.description }} </p>
{{ render(controller("ADVCommentBundle:Comment:newComment" ,{ 'id': post.id,'redirect':'get_post' } )) }}
<hr>
{%endfor%}
I have a trouble with using app.session.flashbag.get('notice').
In the controller I make
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('SomeBundle:SomeEntity')->find($id);
$editForm = $this->createForm(new SomeEntityType(), $entity);
$editForm->bind($request);
if ($editForm->isValid()) {
$em->persist($entity);
$em->flush();
$flash = $this->get('translator')->trans('Some Entity was successfully updated');
$this->get('session')->getFlashBag()->add('notice', $flash);
return $this->redirect($this->generateUrl('some_entity_edit', array('id' => $id)));
}
In editAction I'm getting information from the session:
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$flashes = $this->get('session')->getFlashBag()->get('notice', array());
//...
//...
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'flashes' => $flashes
);
}
And i'm trying in the TWIG get information from the session :
TWIG: {% for flashMessage in app.session.flashbag.get('notice') %}{{ flashMessage }}{% endfor %}
PHP: {% for flashMessage2 in flashes %}{{ flashMessage2 }}{% endfor %}
The app.session.flashbag.get('notice') is empty, the flashes has a value.
Do you have any ideas, why I can't get data from the app.session.flashbag.get('notice')?
Its normal behavior. You access flash in controller first, so it is returned and unset then. When you access it again then key does not exists in flashbag that way is empty.
See FlashBag::get at github
There is an easy way to handle (add/display) Symfony flash messages with FlashAlertBundle, it is standalone Symfony2 bundle implemented with pure JavaScript, so you don't have to worry about using JS libraries.
You just need the following code to render flash messages on your twig template:
{{ render_flash_alerts() }}
Available through
https://github.com/rasanga/FlashAlertBundle
https://packagist.org/packages/ras/flash-alert-bundle
I have a list of friends which should be displayed 3 in a page. Each friend has a category and I also have a drop down menu to choose to view only the friends which are from the chosen category. They should also be display 3 in a page. The way in which filtered and not filtered friends are displayed is the same so I didn't want to have two almost actions in my controller and two identic templates, so I tried to make this in one controller's action and template, but there is a problem. I can't make the pagination for the second and following pages of the filtered friends. Pleae help! :( The problem is that I use a form and when I click on the second page, the variable which was filled in the form and binded, become undefined. Here is the code:
Controller's action
public function displayAction($page, Request $request)
{
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$cat = new Category();
$dd_form = $this->createForm(new ChooseCatType($user->getId()), $cat);
if($request->get('_route')=='filter')
{
if($request->getMethod() == 'POST')
{
$dd_form->bindRequest($request);
if($cat->getName() == null)
{
return $this->redirect($this->generateUrl('home_display'));
}
$filter = $cat->getName()->getId();
if ($dd_form->isValid())
{
$all_friends = $em->getRepository('EMMyFriendsBundle:Friend')
->filterFriends($filter);
$result = count($all_friends);
$FR_PER_PAGE = 3;
$pages = $result/$FR_PER_PAGE;
$friends = $em->getRepository('EMMyFriendsBundle:Friend')
->getFilteredFriendsFromTo($filter, $FR_PER_PAGE, ($page-1)*$FR_PER_PAGE);
$link = 'filter';
}
}
}
else
{
$all_friends = $user->getFriends();
$result = count($all_friends);
$FR_PER_PAGE = 3;
$pages = $result/$FR_PER_PAGE;
$friends = $em->getRepository('EMMyFriendsBundle:Friend')
->getFriendsFromTo($user->getId(), $FR_PER_PAGE, ($page-1)*$FR_PER_PAGE);
$link = 'home_display';
}
// Birthdays
$birthdays = null;
$now = new \DateTime();
$now_day = $now->format('d');
$now_month = $now->format('m');
foreach ($all_friends as $fr)
{
if($fr->getBirthday() != null)
{
if($fr->getBirthday()->format('d') == $now_day && $fr->getBirthday()->format('m') == $now_month)
{
$birthdays[]=$fr;
$fr->setYears();
}
}
}
// Search
$search = new Search();
$s_form = $this->createFormBuilder($search)
->add('words', 'text', array(
'label' => 'Search: ',
'error_bubbling' => true))
->getForm();
// Renders the template
return $this->render('EMMyFriendsBundle:Home:home.html.twig', array(
'name' => $name, 'friends' => $friends, 'user' => $user, 'birthdays' => $birthdays, 'pages' => $pages, 'page' => $page, 'link' => $link,
'dd_form' => $dd_form->createView(), 's_form' => $s_form->createView()));
}
Template
{% if birthdays != null %}
<div>
<img class="birthday" src="http://www.clker.com/cliparts/1/d/a/6/11970917161615154558carlitos_Balloons.svg.med.png">
<div class="try">
This friends have birthday today:
{% for bd in birthdays %}
<p>
{{ bd.name }}
<span class="years">
({{ bd.years }} years)
</span>
</p>
{% endfor %}
</div>
</div>
{% endif %}
{% for fr in friends %}
{# TODO: Fix where are shown #}
{% if fr.getWebPath()!=null %}
<a href="{{ path('friend_id', {'id': fr.id}) }}">
<img class="avatar" src="{{ fr.getWebPath }}">
</a>
{% endif %}
{% if loop.index is odd %}
<p class="list1">
{% else %}
<p class="list2">
{% endif %}
<a class="friends" href="{{ path('friend_id', {'id': fr.id}) }}">{{ fr.name }}</a>
</p>
{% endfor %}
{# TODO: Pagination #}
{% if pages>1 %}
<p>
{% for i in 0..pages %}
{% if page == loop.index %}
<span class="pagination">{{ loop.index }}</span>
{% else %}
<span class="pagination">{{ loop.index }}</span>
{% endif %}
{% endfor %}
</P>
{% endif %}
<p>Choose category:</p>
<form class="search" action="{{ path('filter') }}" method="post" {{ form_enctype(s_form) }}>
{{ form_widget(dd_form.name) }}
{{ form_rest(dd_form) }}
<input type="submit" value="Show friends" />
</form>
Repository
class FriendRepository extends EntityRepository
{
public function getFriendsFromTo ($user, $limit, $offset)
{
return $this->getEntityManager()
->createQuery('SELECT f FROM EMMyFriendsBundle:Friend f WHERE f.user='.$user. 'ORDER BY f.name ASC')
->setMaxResults($limit)
->setFirstResult($offset)
->getResult();
}
public function filterFriends ($filter)
{
$q = $this->createQueryBuilder('f');
$q->select('f')
->where('f.category = :filter')
->setParameter('filter', $filter);
return $q->getQuery()->getResult();
}
public function getFilteredFriendsFromTo ($filter, $limit, $offset)
{
$q = $this->createQueryBuilder('f');
$q->select('f')
->where('f.category = :filter')
->setMaxResults($limit)
->setFirstResult($offset)
->setParameter('filter', $filter);
return $q->getQuery()->getResult();
}
}
I tried a lot of things, but there is always a problem. In this code it says that the variable $all_friends in the birthday for loop is not defined - and yes, it isn't. Maybe I have to store it in session and I tried this:
$session = $this->getRequest()->getSession();
$session->set('all_friends');
and then passing $friends=$session->get('all_friends'); to the for loop, but it doesn't work and isn't the variable $all_friends too big to store it?
Any ideas will be apreciated! Thank you for your time and effort!
EDIT
When I use the way with the session and
$session = $this->getRequest()->getSession();
$session->set('all_friends');
$fri=$session->get('all_friends');
foreach ($fri as $fr)
{ .... }
the error I get is
Warning: Invalid argument supplied for foreach() in C:\xampp\htdocs\MyFriends\src\EM\MyFriendsBundle\Controller\HomeController.php line 100
and also
Warning: Missing argument 2 for Symfony\Component\HttpFoundation\Session::set(), called in C:\xampp\htdocs\MyFriends\src\EM\MyFriendsBundle\Controller\HomeController.php on line 71 and defined in C:\xampp\htdocs\MyFriends\app\cache\dev\classes.php line 148
When I don't use session I get
Notice: Undefined variable: all_friends in C:\xampp\htdocs\MyFriends\src\EM\MyFriendsBundle\Controller\HomeController.php line 100
when I choose a category to show the friends from it, and I click its second page.
P.S. The lines from the errors don't corespond to the lines in the code I pasted, bacause I skipped some parts of the action, repository and template, because they don't have a part in this problem and they work correctly. If someone wishes, I can send him or update here all the code.
You are setting nothing on session :
$session->set('all_friends');
You should be doing this instead:
$session->set('all_friends', $data);
You should really start respecting Symfony2 coding standards too.
My eyes are melting when I try to read your code. You should read this and don't forget to create form class instead of creating form in your controller.
EDIT: If your $data is a result from a Doctrine2 query, I suggest that you store only the entity id and the entity class in order to fetch them later when you need it.
EDIT2: Here's some code that might help you saving on session filters data. PS, don't forget to add the missing use ....
/**
* Set filters
*
* #param array $filters Filters
* #param string $type Type
*/
public function setFilters($name, array $filters = array())
{
foreach ($filters as $key => $value) {
// Transform entities objects into a pair of class/id
if (is_object($value)) {
if ($value instanceof ArrayCollection) {
if (count($value)) {
$filters[$key] = array(
'class' => get_class($value->first()),
'ids' => array()
);
foreach ($value as $v) {
$identifier = $this->getDoctrine()->getManager()->getUnitOfWork()->getEntityIdentifier($v);
$filters[$key]['ids'][] = $identifier['id'];
}
} else {
unset($filters[$key]);
}
} elseif (!$value instanceof \DateTime) {
$filters[$key] = array(
'class' => get_class($value),
'id' => $this->getDoctrine()->getManager()->getUnitOfWork()->getEntityIdentifier($value)
);
}
}
}
$this->getRequest()->getSession()->set(
$name,
$filters
);
}
/**
* Get Filters
*
* #param array $filters Filters
* #param type $type Type
*
* #return array
*/
public function getFilters($name, array $filters = array())
{
$filters = array_merge(
$this->getRequest()->getSession()->get(
$name,
array()
),
$filters
);
foreach ($filters as $key => $value) {
// Get entities from pair of class/id
if (is_array($value) && isset($value['class'])) {
if (isset($value['id'])) {
$filters[$key] = $this->getDoctrine()->getManager()->find($value['class'], $value['id']);
} elseif (isset($value['ids'])) {
$data = $this->getDoctrine()->getManager()->getRepository($value['class'])->findBy(array('id' => $value['ids']));
$filters[$key] = new ArrayCollection($data);
}
}
}
return $filters;
}
EDIT3: Why you're not using KnpPaginatorBundle for pagination ?