Foros del Web » Programando para Internet » PHP » Symfony »

Memoria agotada al renderizar form collection

Estas en el tema de Memoria agotada al renderizar form collection en el foro de Symfony en Foros del Web. Hola. Tengo un formulario con un campo collection de tipo *ListadoDetallesType()* este form type tiene 22 campos, hasta aquí todo bien. El problema viene cuando ...
  #1 (permalink)  
Antiguo 27/04/2015, 10:34
Avatar de anacona16  
Fecha de Ingreso: marzo-2010
Ubicación: Bogota DC
Mensajes: 610
Antigüedad: 9 años, 9 meses
Puntos: 52
Memoria agotada al renderizar form collection

Hola.

Tengo un formulario con un campo collection de tipo *ListadoDetallesType()* este form type tiene 22 campos, hasta aquí todo bien.

El problema viene cuando quiero editar mas de un registro al mismo tiempo, pasando a:

Código PHP:
Ver original
  1. $form = $this->createForm(new DetallesType(), $detalles);

Al hacer esto, cuando los detalles son mas de 30, obtengo el siguiente error:

Código PHP:
Ver original
  1. OutOfMemoryException in Profile.php line 143:
  2. Error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 2779493 bytes)

He notado que el problema se de debe al numero de campos del form type ListadoDetallesType(), porque si le dejo por ejemplo 5 campos no hay problema.

Para pasarle el arreglo de objectos $detalles a createForm me he guiado con un ejemplo que hay aca: http://sf2.showmethecode.es/app.php/...orms/user/edit

El codigo resumido es:

Código PHP:
Ver original
  1. $detalles = $this->get('detalles_repository')->findUltimosDetalles(15);
  2. $detallesContainer = new \AppBundle\Form\Model\Detalles($detalles);
  3.  
  4. $form = $this->createForm(new DetallesType(), $detalles);

Y la clase Detalles es:

Código PHP:
Ver original
  1. namespace AppBundle\Form\Model;
  2.  
  3. use Symfony\Component\Validator\Constraints as Assert;
  4.  
  5. class Nota
  6. {
  7.     /**
  8.      * @Assert\Valid()
  9.      */
  10.     public $notas;
  11.  
  12.     public function __construct($notas)
  13.     {
  14.         $this->notas = $notas;
  15.     }
  16. }

DetallesType.php:

Código PHP:
Ver original
  1. ...
  2. public function buildForm(FormBuilderInterface $builder, array $options)
  3. {
  4.     $builder
  5.         ->add('detalles', 'collection', [
  6.             'type' => new ListadoDetallesType(),
  7.         ])
  8.     ;
  9. }
  10. ...

ListadoDetallesType.php:

Código PHP:
Ver original
  1. public function buildForm(FormBuilderInterface $builder, array $options)
  2. {
  3.     $builder
  4.         ->add('detalle1', null, [])
  5.         .... // 22 campos mas
  6.     ;
  7. }

En la plantilla twig:

Código HTML:
Ver original
  1. <form method="POST" role="form" {{ form_enctype(form) }}>
  2.     {{ form_widget(form) }}
  3.     <button type="submit" class="btn btn-success">Guardar</button>
  4. </form>

Al final esto se renderiza asi:
http://postimg.org/image/vnuwn1ubf/

Que debo hacer para evitar ese error? ¿O debo hacer este tipo de formulario de otra manera?

Espero que me puedan ayudar.

Gracias.
__________________
Aprendiendo!!!
  #2 (permalink)  
Antiguo 27/04/2015, 11:53
Avatar de hhs
hhs
Colaborador
 
Fecha de Ingreso: junio-2013
Ubicación: México
Mensajes: 2.995
Antigüedad: 6 años, 5 meses
Puntos: 379
Respuesta: Memoria agotada al renderizar form collection

Un punto importante siempre sigue los tutoriales oficiales el código que mencionas ya es algo viejo.
http://symfony.com/doc/current/cookb...llections.html
Por otro lado si el detalle esta en una relación 1:N no tienes que pasar la colección en una consulta adicional, puedes traer tu entidad con su detalle en una sola consulta con DQL o el QueryBuilder es probable que este tomando mas tiempo el llenado de los types por este motivo.
Cita:
Que debo hacer para evitar ese error? ¿O debo hacer este tipo de formulario de otra manera?
Generalmente existe otra forma de crear el formulario, pero eso depende de lo que necesitas hacer. También es valido que prescindas de el componente de formularios si asi lo crees conveniente.
Que cantidad de memoria limite le diste a php ?
__________________
Saludos
About me
Laraveles
A class should have only one reason to change.
  #3 (permalink)  
Antiguo 27/04/2015, 12:41
Avatar de anacona16  
Fecha de Ingreso: marzo-2010
Ubicación: Bogota DC
Mensajes: 610
Antigüedad: 9 años, 9 meses
Puntos: 52
Respuesta: Memoria agotada al renderizar form collection

Hola hhs gracias por tu respuesta

Estoy de acuerdo y casi siempre sigo la doc oficial. El problema para este caso es que en la doc oficial tratan el escenario donde editar un registro con sus detalles, para esto no hay problema.

En lo que trato de hacer es editar mas de un registro a las vez, y estos registro a su vez tienen una relación 1:N

Intente pasarle a la función $this->createForm(new Type(), $productos);

Pero, me arroja un error diciendo que createForm espera un instancia de Productos, mientras que un array fue dado, por esa razón use la clase Model que la tome de ejemplo del link que mencione.

Existe otra forma de parle mas de un registro a createForm()?

En lo personal pienso que debería usar siempre los componente del framework, es decir, si ya existe un componente de forms, cosas como estas se deberían poder hacer sin problemas...

Espero me entiendas...

La memoria de PHP es de 128MB
__________________
Aprendiendo!!!
  #4 (permalink)  
Antiguo 27/04/2015, 16:38
Avatar de hhs
hhs
Colaborador
 
Fecha de Ingreso: junio-2013
Ubicación: México
Mensajes: 2.995
Antigüedad: 6 años, 5 meses
Puntos: 379
Respuesta: Memoria agotada al renderizar form collection

Anacona puedes publicar tus entidades para darme una idea de ellas y como tienes los Types vaya algo un poco mas completo. Según entiendo tienes un "producto"que tiene N características hasta un total de 22 cierto ?
Las características de que tipo de dato son ? vas a realizar consultas que involucren a estas características ?
__________________
Saludos
About me
Laraveles
A class should have only one reason to change.
  #5 (permalink)  
Antiguo 27/04/2015, 17:06
Avatar de anacona16  
Fecha de Ingreso: marzo-2010
Ubicación: Bogota DC
Mensajes: 610
Antigüedad: 9 años, 9 meses
Puntos: 52
Respuesta: Memoria agotada al renderizar form collection

Hola hhs

Aqui la entidad Producto

Código PHP:
Ver original
  1. <?php
  2.  
  3. namespace AppBundle\Entity;
  4.  
  5. use Doctrine\ORM\Mapping as ORM;
  6.  
  7. /**
  8.  * Producto
  9.  *
  10.  * @ORM\Table()
  11.  * @ORM\Entity
  12.  */
  13. class Producto
  14. {
  15.     /**
  16.      * @var integer
  17.      *
  18.      * @ORM\Column(name="id", type="integer")
  19.      * @ORM\Id
  20.      * @ORM\GeneratedValue(strategy="AUTO")
  21.      */
  22.     private $id;
  23.  
  24.     /**
  25.      * @var string
  26.      *
  27.      * @ORM\Column(name="nombre", type="string", length=255)
  28.      */
  29.     private $nombre;
  30.  
  31.     /**
  32.      * @var string
  33.      *
  34.      * @ORM\OneToMany(targetEntity="AppBundle\Entity\DetallesProducto", mappedBy="producto")
  35.      */
  36.     private $detalles;
  37.  
  38.     /**
  39.      * Constructor
  40.      */
  41.     public function __construct()
  42.     {
  43.         $this->detalles = new \Doctrine\Common\Collections\ArrayCollection();
  44.     }
  45.  
  46.     /**
  47.      * Get id
  48.      *
  49.      * @return integer
  50.      */
  51.     public function getId()
  52.     {
  53.         return $this->id;
  54.     }
  55.  
  56.     /**
  57.      * Set nombre
  58.      *
  59.      * @param string $nombre
  60.      * @return Producto
  61.      */
  62.     public function setNombre($nombre)
  63.     {
  64.         $this->nombre = $nombre;
  65.  
  66.         return $this;
  67.     }
  68.  
  69.     /**
  70.      * Get nombre
  71.      *
  72.      * @return string
  73.      */
  74.     public function getNombre()
  75.     {
  76.         return $this->nombre;
  77.     }
  78.  
  79.     /**
  80.      * Set caracteristicas
  81.      *
  82.      * @param string $caracteristicas
  83.      * @return Producto
  84.      */
  85.     public function setCaracteristicas($caracteristicas)
  86.     {
  87.         $this->caracteristicas = $caracteristicas;
  88.  
  89.         return $this;
  90.     }
  91.  
  92.     /**
  93.      * Get caracteristicas
  94.      *
  95.      * @return string
  96.      */
  97.     public function getCaracteristicas()
  98.     {
  99.         return $this->caracteristicas;
  100.     }
  101.  
  102.     /**
  103.      * Add detalles
  104.      *
  105.      * @param \AppBundle\Entity\DetallesProducto $detalles
  106.      * @return Producto
  107.      */
  108.     public function addDetalle(\AppBundle\Entity\DetallesProducto $detalles)
  109.     {
  110.         $this->detalles[] = $detalles;
  111.  
  112.         return $this;
  113.     }
  114.  
  115.     /**
  116.      * Remove detalles
  117.      *
  118.      * @param \AppBundle\Entity\DetallesProducto $detalles
  119.      */
  120.     public function removeDetalle(\AppBundle\Entity\DetallesProducto $detalles)
  121.     {
  122.         $this->detalles->removeElement($detalles);
  123.     }
  124.  
  125.     /**
  126.      * Get detalles
  127.      *
  128.      * @return \Doctrine\Common\Collections\Collection
  129.      */
  130.     public function getDetalles()
  131.     {
  132.         return $this->detalles;
  133.     }
  134. }

Aqui la entidad DetallesProducto, esta entidad tiene 22 propiedades, solo puse 5 para simplificar el codigo...

Código PHP:
Ver original
  1. <?php
  2.  
  3. namespace AppBundle\Entity;
  4.  
  5. use Doctrine\ORM\Mapping as ORM;
  6.  
  7. /**
  8.  * DetallesProducto
  9.  *
  10.  * @ORM\Table()
  11.  * @ORM\Entity
  12.  */
  13. class DetallesProducto
  14. {
  15.     /**
  16.      * @var integer
  17.      *
  18.      * @ORM\Column(name="id", type="integer")
  19.      * @ORM\Id
  20.      * @ORM\GeneratedValue(strategy="AUTO")
  21.      */
  22.     private $id;
  23.  
  24.     /**
  25.      * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Producto", inversedBy="detalles")
  26.      * @ORM\JoinColumn(name="producto_id", referencedColumnName="id")
  27.      */
  28.     private $producto;
  29.  
  30.     /**
  31.      * @var string
  32.      *
  33.      * @ORM\Column(name="detalle1", type="integer")
  34.      */
  35.     private $detalle1;
  36.  
  37.     /**
  38.      * @var integer
  39.      *
  40.      * @ORM\Column(name="detalle2", type="integer")
  41.      */
  42.     private $detalle2;
  43.  
  44.     /**
  45.      * @var integer
  46.      *
  47.      * @ORM\Column(name="detalle3", type="integer")
  48.      */
  49.     private $detalle3;
  50.  
  51.  
  52.     /**
  53.      * Get id
  54.      *
  55.      * @return integer
  56.      */
  57.     public function getId()
  58.     {
  59.         return $this->id;
  60.     }
  61.  
  62.     /**
  63.      * Set detalle1
  64.      *
  65.      * @param string $detalle1
  66.      * @return CaracteristicasProducto
  67.      */
  68.     public function setDetalle1($detalle1)
  69.     {
  70.         $this->detalle1 = $detalle1;
  71.  
  72.         return $this;
  73.     }
  74.  
  75.     /**
  76.      * Get detalle1
  77.      *
  78.      * @return string
  79.      */
  80.     public function getDetalle1()
  81.     {
  82.         return $this->detalle1;
  83.     }
  84.  
  85.     /**
  86.      * Set detalle2
  87.      *
  88.      * @param integer $detalle2
  89.      * @return CaracteristicasProducto
  90.      */
  91.     public function setDetalle2($detalle2)
  92.     {
  93.         $this->detalle2 = $detalle2;
  94.  
  95.         return $this;
  96.     }
  97.  
  98.     /**
  99.      * Get detalle2
  100.      *
  101.      * @return integer
  102.      */
  103.     public function getDetalle2()
  104.     {
  105.         return $this->detalle2;
  106.     }
  107.  
  108.     /**
  109.      * Set detalle3
  110.      *
  111.      * @param integer $detalle3
  112.      * @return CaracteristicasProducto
  113.      */
  114.     public function setDetalle3($detalle3)
  115.     {
  116.         $this->detalle3 = $detalle3;
  117.  
  118.         return $this;
  119.     }
  120.  
  121.     /**
  122.      * Get detalle3
  123.      *
  124.      * @return integer
  125.      */
  126.     public function getDetalle3()
  127.     {
  128.         return $this->detalle3;
  129.     }
  130.  
  131.     /**
  132.      * Set producto
  133.      *
  134.      * @param \AppBundle\Entity\Producto $producto
  135.      * @return CaracteristicasProducto
  136.      */
  137.     public function setProducto(\AppBundle\Entity\Producto $producto = null)
  138.     {
  139.         $this->producto = $producto;
  140.  
  141.         return $this;
  142.     }
  143.  
  144.     /**
  145.      * Get producto
  146.      *
  147.      * @return \AppBundle\Entity\Producto
  148.      */
  149.     public function getProducto()
  150.     {
  151.         return $this->producto;
  152.     }
  153. }

Este es el ProductoType

Código PHP:
Ver original
  1. <?php
  2.  
  3. namespace AppBundle\Form;
  4.  
  5. use Symfony\Component\Form\AbstractType;
  6. use Symfony\Component\Form\FormBuilderInterface;
  7. use Symfony\Component\OptionsResolver\OptionsResolverInterface;
  8.  
  9. class ProductoType extends AbstractType
  10. {
  11.     /**
  12.      * @param FormBuilderInterface $builder
  13.      * @param array $options
  14.      */
  15.     public function buildForm(FormBuilderInterface $builder, array $options)
  16.     {
  17.         $builder
  18.             ->add('detalles', 'collection', [
  19.                 'type' => new DetallesProductoType()
  20.             ])
  21.         ;
  22.     }
  23.    
  24.     /**
  25.      * @param OptionsResolverInterface $resolver
  26.      */
  27.     public function setDefaultOptions(OptionsResolverInterface $resolver)
  28.     {
  29.         $resolver->setDefaults(array(
  30.             'data_class' => 'AppBundle\Entity\Producto'
  31.         ));
  32.     }
  33.  
  34.     /**
  35.      * @return string
  36.      */
  37.     public function getName()
  38.     {
  39.         return 'appbundle_producto';
  40.     }
  41. }

Este el DetallesProductoType

Código PHP:
Ver original
  1. <?php
  2.  
  3. namespace AppBundle\Form;
  4.  
  5. use Symfony\Component\Form\AbstractType;
  6. use Symfony\Component\Form\FormBuilderInterface;
  7. use Symfony\Component\OptionsResolver\OptionsResolverInterface;
  8.  
  9. class DetallesProductoType extends AbstractType [I]se agregan todos los campos, hasta detalleN[/I]
  10. {
  11.     /**
  12.      * @param FormBuilderInterface $builder
  13.      * @param array $options
  14.      */
  15.     public function buildForm(FormBuilderInterface $builder, array $options)
  16.     {
  17.         $builder
  18.             ->add('detalle1')
  19.             ->add('detalle2')
  20.             ->add('detalle3')
  21.             ->add('detalle4')
  22.             ->add('detalle5')
  23.         ;
  24.     }
  25.    
  26.     /**
  27.      * @param OptionsResolverInterface $resolver
  28.      */
  29.     public function setDefaultOptions(OptionsResolverInterface $resolver)
  30.     {
  31.         $resolver->setDefaults(array(
  32.             'data_class' => 'AppBundle\Entity\DetallesProducto'
  33.         ));
  34.     }
  35.  
  36.     /**
  37.      * @return string
  38.      */
  39.     public function getName()
  40.     {
  41.         return 'appbundle_detallesproducto';
  42.     }
  43. }

Hasta aquí todo bien, es decir que desde un controller puedo hacer esto:

Código PHP:
Ver original
  1. $em = $this->getDoctrine()->getManager();
  2.  
  3. $producto = $em->getRepository('AppBundle:Post')->find(1);
  4.  
  5. $form = $this->createForm(new ProductoType(), $producto);

En la plantilla:

Código HTML:
Ver original
  1. <form method="POST" role="form" {{ form_enctype(form) }}>
  2.     {{ form_widget(form) }}
  3.     <button type="submit" class="btn btn-success">Guardar</button>
  4. </form>

Funciona perfectamente, me renderiza la entidad y sus detalles, de tal forma que puedo agregar o quitar elementos.

Voy a dejar de una lado lo que tengo y preguntar como haria lo que quiero hacer, quiza esto aclare un poco las cosas.

Lo que necesito es editar mas de un producto al mismo tiempo, es decir:

Código PHP:
Ver original
  1. $em = $this->getDoctrine()->getManager();
  2.  
  3. // Consulto los ultimos 30 productos
  4. $productos = $em->getRepository('AppBundle:Post')->findUltimosProductos(30);
  5.  
  6. $form = $this->createForm(new ProductoType(), $productos);

Al imprimir el form en la plantilla, obtengo un error diciendo que createForm requiere una instancia de Producto, mientras que un array se paso como argumento.

La pregunta es: ¿Como puedo editar mas de una entidad al mismo tiempo?

PD: Si deseas puedo poner el codigo que utilizo para solucionar este problema, el inconveniente con esa solucion es que cuando son mas de 20 campos detalles, arroja el error de memoria.

Gracias.
__________________
Aprendiendo!!!
  #6 (permalink)  
Antiguo 27/04/2015, 18:57
Avatar de hhs
hhs
Colaborador
 
Fecha de Ingreso: junio-2013
Ubicación: México
Mensajes: 2.995
Antigüedad: 6 años, 5 meses
Puntos: 379
Respuesta: Memoria agotada al renderizar form collection

Ya entendí, tu quieres editar "inline" pero para este tipo de panoramas es mucho mejor utilizar un grid como datatable u otro producto de este tipo.
El problema con los formulario es que se crean en estructura de árbol y cada type es un nodo así que si tu solicitas 15 productos tienes un árbol de dos niveles con 20 nodos por cada nodo de productos el tiempo que le va tomar llenar y recorrer cada nodo es lo que te esta dando problemas en este caso.

Si quieres hacerlo utilizando el form de todas formas, necesitas reducir el conjunto de elementos que presentas, agrupando su presentación de 5 a 10 elementos por vez. Para esto vas a Tener que crearte un type que haga "paginación".
Para reducir tiempo cambia la relación de producto->detalle a 1:1 ya que un producto solo tiene un conjunto de características.
Si lo quieres 1:N tienes dos opciones: Que detalle sea de tipo Array este seria el mejor caso y reduciría la consulta y la carga pero no podrías hacer búsquedas especificas por esos campos.
La otra opciones es que uses el esquema KV; cada renglón es una propiedad, de esa forma las consultas no están obligadas a rescatar los 20 campos estén o no llenos y el form no tiene que intentar llenar 20 propiedades de forma obligada.

Esto seria a groso modo lo que puedes hacer de momento.
__________________
Saludos
About me
Laraveles
A class should have only one reason to change.

Etiquetas: collection, form, memoria, renderizar
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 12:24.