Este foro ya no está activo, así que no puedes publicar nuevas preguntas ni responder a las preguntas existentes.

Eliminar foto con sonata_type_colletion

25 de octubre de 2014

Muy buenas,

La verdad es que ya no puedo más con esto, me tiene un poco desesperado.

Mi problema es que en el administrador con SonataAdminBundle no puede eliminar ningún campo de la base de datos imágenes y lo más que me mosquea es que tengo otro hecho igual con vídeos y funciona correctamente. Bueno en el de vídeo son solamente enlaces a YouTube, pero es lo mismo. Porque aunque no me eliminara la foto de la carpeta, me podría eliminar el registro.

Expongo mi código haber si me podéis sacar de este aprieto que llevo tres días, para esto leyendo, probando y probando.

Tengo dos entidades una foto y otra messages.

Entidad Fotos:

/**
 * Fotos
 *
 * @ORM\Table(name="fotos")
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class Fotos
{
 
    /**
     * @ORM\ManyToOne(targetEntity="Acme\MessagesBundle\Entity\Messages", inversedBy="fotos")
     * @ORM\JoinColumn(name="message_id", referencedColumnName="id")
     */
    private $messages;
 
    // ...
}

Código para FotosAdmin.php

class FotosAdmin extends Admin
{
    /**
     * @param FormMapper $formMapper
     */
    protected function configureFormFields(FormMapper $formMapper)
    {
        $formMapper
            ->add('foto', 'file',array(
                'required' => false,
                'help' => $rutaFoto,
                'label' => 'Foto '.$numero.' ',
                'attr' => array(
                    'class' => 'nice_file_field',
                    // 'onchange'=>"readImage(this.form,this)",
                    // 'oninvalid'=>"setCustomValidity('Por favor escoge un nombre de usuario valido')"
                )
            ))
 
            ->add('nombre','text',array(
                'label' => 'Nombre la foto:',
                'required' => false,
            ))
        ;
    }
 
    // ...
}

Continua en la siguiente respuesta...


Respuestas

#1

Continúo con el código, porque me ha dado un error que la pregunta no puede ser tan larga.

Entidad messages:

/**
 * Messages
 *
 * @ORM\Table(name="messages")
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
 
class Messages
{
    /**
     * @var ArrayCollection $fotos
     *
     * @ORM\OneToMany(
     * targetEntity="Acme\FotosBundle\Entity\Fotos",
     * mappedBy="messages",
     * cascade={"all"})
     * orphanRemoval=true)
     *@Assert\Valid()
     *
     */ 
    private $fotos;
 
    /**
     * @var ArrayCollection $videos
     *
     * @ORM\OneToMany(
     * targetEntity="Acme\VideosBundle\Entity\Videos",
     * mappedBy="messages",
     * cascade={"all"},
     * orphanRemoval=true)
     *@Assert\Valid()
     *
     */ 
    private $videos;
 
    // ...
 
    public function __construct() {
        $this->fotos  = new ArrayCollection();
        $this->videos = new ArrayCollection();
    }
 
    // ...
 
    /**
     * Set fotos
     *
     * @param string $fotos
     * @return messages
     */
    public function setFotos($fotos = null)
    {
        foreach ($fotos as $image) {
        $image->setMessages($this);
    }
 
    public function addFotos(\Acme\FotosBundle\Entity\Fotos $fotos)
    {
        if(!$this->fotos->contains($fotos)){
            $this->fotos[] = $fotos;
            $fotos->setMessages($this);
 
        }
        return $this;
    }
 
    public function removeFotos(\Acme\FotosBundle\Entity\Fotos $fotos)
    {
        $this->fotos->removeElement($fotos);
        $this->fotos->remove($fotos);
        $fotos->setMessages(null);
        if ($file = $this->fotos->getUploadRootDir($id)) {
            unlink($file);
        }
    }
 
    public function removeVideos(\Acme\VideosBundle\Entity\Videos $videos)
    {
        $this->videos->removeElement($videos);
    }
 
    // ...
}

Formulario MessagesAdmin.php para SonataAdmin:

class MessagesAdmin extends Admin
{
    /**
     * @param FormMapper $formMapper
     */
    protected function configureFormFields(FormMapper $formMapper)
    {
 
        // ...
 
        ->Tab('Fotos', array('value'=>'Fotos'))  
            ->with('Fotos', array(
                'class'       => 'col-md-12',
                'description' => '',
                'name'        => 'Fotos'
            ))
            ->add('fotos', 'sonata_type_collection', array(
                'by_reference' => false, 
                //'btn_add' => false,
                'required' => false,
                'cascade_validation' => true,
                'label' => 'Fotos experiencias',
                'type_options' => array('delete' => true,)
            ), array(
                'edit' => 'inline',
               // 'inline' => 'table',
                'sortable' => 'position',
            ))                
            ->end()
        ->end()
 
    // ...

Bueno pues cuando marco el checkbox de Delete y lo doy actualizar no hace nada, lo peor de todo es que ni siquiera me da un error. Otra cosa la subida de imagen me va bien.

Pongo una captura de la imagen del administrador.

@jcarlosweb

25 octubre 2014, 12:32
#2

Muy buenas otra vez,

La verdad me gustaría saber si las preguntas que hago son muy obvias o son inutiles.

Ya que al no tener respuesta, no se donde publicarlo y no se quien me podría ayudar.

La unica ayuda que tengo es internet ya que soy autodidacta y Symfony lo estoy aprendiendo así. No si sabeís si para este tipo de preguntas, ¿donde puedo hacerlo?.

Y que me da igual pagar mi unica intencion es aprender.

Un saludo.

@jcarlosweb

25 octubre 2014, 21:29
#3

Buenas otra vez,

Ahora sí que me sale error por lo menos. Por lo que se ve no puedo conseguir el id del mensaje relacionado con la foto cuando le doy a eliminar. Me sale el siguiente error:

Error: Call to a member function getId() on a non-object in /var/www/proyecto/src/acme/FotosBundle/Entity/Fotos.php line 650

Y la linea 650 es esto donde obtengo la ruta de la foto:

/**
 * Get rutaFoto
 *
 * @return string
 */
public function getRutaFoto()
{
    $ruta="fotos/messages/".$this->getMessages()->getId()."/".$this->rutaFoto;   
 
    return $ruta;
}

Sabéis porque no puedo conseguir el id en la entidad fotos. La verdad que esto me funciona en todo menos en esto, a la hora de eliminar.

Un saludo.

@jcarlosweb

24 noviembre 2014, 0:02
#4

Perfecto ya he conseguido sacar el id del mensaje, solamente tenia que crear los setter y getter del campo de relación con el mensaje.

Ahora el problema esta que no se como eliminarlo ya que el problema que tengo no lo puedo solucionar con HasLifecycleCallbacks y PostRemove, ya que esto se ejecuta cuando elimino el mensaje y rollo esta que al administrar el mensaje expongo las fotos y las pre visualizo bien y puedo subir más foto, pero no puedo eliminar, al igual que hago con los videos. Claro los vídeos son campos de textos pero las fotos es solamente un registro que tengo que eliminar de la tabla fotos para eliminar la relación con el mensaje. Es solo eso.

Por ahora he conseguido sacar en el PreUpdate MessagesAdmin.php la foto que viene con la variable delete de esta manera:

public function preUpdate($objectif) {
 $params             =  $this->getRequest()->request->get($this->getUniqid());
    $images              =  $params['fotos'];
 
    foreach ($images as $key=>$value) {
 
      if(isset($images[$key]["_delete"]))
          echo "yes";
die();
}

Pero el único data que obtengo es esto

Content-Disposition: form-data; name="s5474db1dc479a[fotos][2][_delete]"

que me lo pasa por Post porque lo he mirado por el Chrome, pero ahora no se como eliminarlo, no tengo ninguna solución ni con HasLifecycleCallbacks ni nada, ni con la funcion removeFotos de la entidad ni nada.

La verdad estoy haciendo otras cosas de programación, pero llevo ya casi con esto un mes.

Haber si alguien me saca de este embrollo porque la verdad, no se como una cosa tan normal no esta resuelta por ningún sitio.

Gracias!!!

@jcarlosweb

25 noviembre 2014, 20:56
#5

Buenas,

Prueba a configurar en la asociación OneToMany lo siguiente:

orphan-removal

Saludos!

@manuel_j555

25 noviembre 2014, 21:45
#6

Gracias @manuel_j555,

Pero ya lo tengo hecho en la entidad Messages.php de este modo

/**
     * @var ArrayCollection $fotos
     *
     * @ORM\OneToMany(
     * targetEntity="Acme\FotosBundle\Entity\Fotos",
     * mappedBy="messages",
     * cascade={"persist", "remove"},
     * orphanRemoval=true)
     *@Assert\Valid()
     *
     */ 
    private $fotos;
 
  // continua ...
 
    public function removeFotos(\Acme\FotosBundle\Entity\Fotos $fotos)
    {
 
       die("hola");
 
        $this->fotos->removeElement($fotos);
        $this->fotos->remove($fotos);
        $fotos->setMessages(null);
 
        if ($file = $this->fotos->getUploadRootDir($id)) {
                unlink($file);
            }
    }

Pero la función removeFotos ni siquiera si ejecuta ya que no me lanza ni el die();

Gracias por la ayuda.

@jcarlosweb

25 noviembre 2014, 22:28
#7

A lo siento, no lo habia visto, bueno prueba a jugar con la opción by_reference del tipo collection.

Generalmente con la opción by_reference en false, lo que se hace es forzar el llamado de los add y remove de la clase padre, en este caso de Messages.

Acá lo explican mejor http://symfony.com/doc/current/cookbook/form/form_collections.html

Saludos!

@manuel_j555

26 noviembre 2014, 4:45
#8

@manuel_j555 eres grande!!! Gracias!!!!! Me funciona con 'by_reference' => true, la verdad no entiendo porque me funciona, sinceramente, porque en la llamada en la funcion removeFotos() le tengo puesto die(); y no se ejecuta, entonces la verdad no se donde proviene la función que elimina el registro, porque si me gustaría perfilarlo y eliminar la imagen del directorio.

Muchas gracias!!!

@jcarlosweb

26 noviembre 2014, 22:48
#9

Con by_reference en true?, por defecto está en true, por lo que si lo has puesto como true, realmente no estás haciendo ningún cambio.

Pero en fin, yo creería que el by_reference es más para usarlo en la funcionalidad de agregar que en la de remover, ya que al remover, la clase PersistentCollection fuerza que el elemento removido sea eliminado de la base de datos. uses o no by_reference.

La gracia del by_reference en estos casos es que en true hace algo como:

//para agregar
$message->getFotos()->add($foto);
//para remover
$message->getFotos()->remove($foto);

mientras que en false hace:

//para agregar
$message->addFoto($foto);
//para remover
$message->removeFoto($foto);

Lo que tambien hay que tener en cuenta es si ese método removeFotos lo has creado tú o lo ha creado Symfony con el comando generate entities, ya que cuando el nombre de la propiedad termina con s hay casos (mas bien creo que siempre) donde esa s del final es removida. por tanto puede que el método sea removeFoto en vez de removeFotos

@manuel_j555

27 noviembre 2014, 2:06
#10

Hola Manuel,

Gracias de nuevo. Manuel lo tenía en false. Según vi en algunos tutoriales o algunos foros.

Ahora estoy en el trabajo, pero en cuanto llegue a casa ya que Symfony es una cosa propia, lo miro y te comento sobre el caso de la "s".

Muchas gracias de nuevo Manuel.

@jcarlosweb

27 noviembre 2014, 8:58
#11

Hola de nuevo @manuel_j555,

No encuentro donde se ejecuta la eliminación de la foto, he probado con y sin "s" y ademas en todas las entidades que tienen relación con las fotos y añadirle las dos funciones con y sin s.

¿Pude ser al ser tipo sonata_type_collection me lo ejecute una función interna de sonata? ¿Cuál puede ser? Esto lo que añado al formulario:

->add('fotos', 'sonata_type_collection', array(
             'by_reference' => true, 
             'mapped'       => true,
             'required'     => false,
             'cascade_validation' => true,
             'label' => 'Fotos experiencias',
                'type_options' => array(
                    'delete' => true,
                )
            ), array(
                'edit' => 'inline',
                'sortable' => 'position',
            ))                
->end()

Gracias y un abrazo!!

@jcarlosweb

27 noviembre 2014, 21:48
#12

Hola,

Con el 'by_reference' => true, no va a llamar nunca a el removeFoto de la entidad. Debe estar en false para que lo haga, por lo que te recomiendo que pruebes lo de la s con el 'by_reference' => false.

Una mejor opción sería eliminar esos métodos add y remove de la entidad, y ejecutar el comando de consola doctrine:generate:entities Bundle:Message para que el framework te los genere correctamente.

Por otro lado, puedes crear un callback en la entidad \Acme\FotosBundle\Entity\Fotos para el evento PostRemove de Doctrine:

/**
 * @ORM\PostRemove()
 */
public function removeImage()
{
    // aquí la lógica para eliminar la imagen.
}

@manuel_j555

28 noviembre 2014, 16:19
#13

Manuel tenias totalmente la razón. He aprendido mucho de esta lección que me has dado. Al final he generado de nuevo los getter y setter. Y si eran si la "s" y yo lo había puesto a mano. Luego elimino el registro del foto perfectamente y con PostRemove lo elimino del directorio.

Ahora no me gustaría abusar de tu ayuda, pero tengo un problema todavía con las colecciones: cuando le doy botón de "Añadir nueva foto" y examino la foto y luego sin actualizar el mensaje le doy añadir de nuevo "Añadir nueva foto" y se pierde el valor de de la primera collection que he añadido, tengo que volver a examinar la foto para que suba la foto correctamente.

¿Sabes a que puede ser debido?

Solamente decirte que muchas gracias, creo que sin ti nunca lo hubiera conseguido.

Un abrazo muy fuerte @manuel_j555

@jcarlosweb

29 noviembre 2014, 22:57
#14

De nada hermano :)

Bueno, creo que eso que comentas es JavaScript, por lo que sería bueno que expongas el código para poder ayudarte mejor, aunque creería que mejor te crees un nuevo post para no ligar las cosas.

@manuel_j555

1 diciembre 2014, 15:39
#15

Gracias Hermano. Eso haré.

@jcarlosweb

2 diciembre 2014, 11:12
#16

Hola, gracias por sus respuestas me ayudaron a darle solución a un problema similar. Gracias por compartir!

@jmarioromero

15 octubre 2016, 15:24