Select specific fields from entity and it relation - symfony

I have many entities with it relations and due to optimize REST calls to select only this fields that I need actually also with entities relations.
So, I have entity like this:
/**
* #ORM\Entity(repositoryClass="App\Repository\DocumentRepository")
* #ORM\Table(name="document_types")
*/
class DocumentType
{
use RestDefaultsTrait;
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
* #Serializer\Groups({"main-document-type"})
*/
private $id;
/**
* #ORM\Column(type="string", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $slug;
/**
* #ORM\Column(type="string", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $symbol;
/**
* #ORM\Column(type="string")
* #Serializer\Groups({"main-document-type"})
*/
private $name;
/**
* #ORM\Column(type="string", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $description;
/**
* #ORM\Column(type="boolean", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $canBeGenerated;
/**
* #ORM\Column(type="boolean", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $inEmployer;
/**
* #ORM\Column(type="boolean", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $inWorker;
/**
* #ORM\Column(type="boolean", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $inAll;
/**
* #ORM\Column(type="boolean", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $isAnnex;
/**
* #ORM\Column(type="boolean", nullable=true)
* #Serializer\Groups({"main-document-type"})
*
*
*/
private $isAttachment;
/**
* #ORM\ManyToOne(targetEntity="DocumentType", inversedBy="childDocuments")
* #Serializer\Groups({"main-document-type"})
*/
private $parentDocument;
/**
* #ORM\OneToMany(targetEntity="DocumentType", mappedBy="parentDocument")
* #Serializer\Groups({"main-document-type"})
*
*/
private $childDocuments;
/**
* #ORM\Column(type="string", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $annexNumberStart;
/**
* #ORM\ManyToOne(targetEntity="ProfessionGroup", inversedBy="documentTypes")
* #Serializer\Groups({"main-document-type"})
*/
private $professionGroup;
/**
* #ORM\Column(type="boolean", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $isNumbering;
/**
* #ORM\Column(type="array", nullable=true)
*/
private $employmentGroup = [];
/**
* #ORM\Column(type="boolean", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $canNumberBeSuggested = false;
/**
* #ORM\Column(type="boolean", nullable=true)
* #Serializer\Groups({"main-document-type"})
*/
private $canBeEdited = false;
}
And in entity repository i want to do something like this:
public function getDocuments( $filterData )
{
$select = [
// 'id' => 'id',
'slug' => 'slug',
'name' => 'name',
'isAnnex' => 'is_annex',
'canNumberBeSuggested' => 'can_number_be_suggested',
'canBeEdited' => 'can_be_edited',
];
$childDocumentsSelect = [
'id' => 'id',
'name' => 'name',
];
$qb = $this->createQueryBuilder( 'document' )
->where( 'document.canBeGenerated = 1' )
;
$qb->select( 'document.id AS id' );
foreach ( $select as $selectField => $selectValue )
{
$qb->addSelect( sprintf( 'document.%s AS %s', $selectField, $selectValue ) );
}
$qb->join( 'document.childDocuments', 'childDocuments' );
foreach ( $childDocumentsSelect as $selectField => $selectValue )
{
$qb->addSelect( sprintf( 'childDocuments.%s AS %s', $selectField, $selectValue ) );
}
return $qb->getQuery()->getResult();
}
with in results I want to recieve base fields from $select array and an array of child documents with selected fields in $childDocumentsSelect in this example only id and name
Is even possible? Sometimes my entities have 3 associations sometimes none. At this moment I'm using serializer but want to optimalize this

Try my example, I think this is what you need.
$qb = $this->createQueryBuilder( 'document' );
$qb->select([
'document.slug',
'document.name',
'document.isAnnex',
'document.canNumberBeSuggested',
'document.canBeEdited',
'childDocument.id',
'childDocument.name'
])
->leftJoin('documnet.childDocuments', 'childDocument')
->where('document.canBeGenerated = 1');
$queryResult = $qb->getQuery()->getResult();
$items = [];
foreach($queryResult as $item) {
$id = $item['id'];
if (!isset($items[$id])) {
$items[$id]['id'] = $id;
}
$items[$id]['child'][] = [
'slug' => $item['name']
];
}
var_dump($items);

Related

Symfony2 OneToMany relation

I'm doing an AdvertPlatform with Symfony 2.5 and PHP 5.3.
I want to add a feature that is : when a user see(by clicking on it) an advert, we can see a text "Seen by : {{username}}".
I started add a new Entity nammed AdvertReader with a relation OneToMany between Advert and AdvertReader:
Advert.php:
/**
* Advert
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Society\PerfclientBundle\Entity\AdvertRepository")
* #ORM\HasLifecycleCallbacks()
* #UniqueEntity(fields="title", message="Une annonce existe déjà avec ce titre.")
*/
class Advert
{
public function __construct()
{
$this->date = new \Datetime();
}
/**
*
* #ORM\OneToMany(targetEntity="Society\PerfclientBundle\Entity\AdvertReader", mappedBy="advert", cascade={"persist"})
*/
private $readers;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255, unique=true)
*
* #Assert\Length(min=10, minMessage="Le titre de l'annonce doit faire au moins {{ limit }} caractères.")
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="category", type="string", length=255)
*/
private $category;
/**
* #var string
*
* #ORM\Column(name="author", type="string", length=255)
*/
private $author;
/**
* #var string
*
* #ORM\Column(name="content", type="text")
* #Assert\Length(min=10, minMessage="Le contenu de l'annonce doit faire au moins {{ limit }} caractères.")
*/
private $content;
/**
* #ORM\Column(name="updated_at", type="datetime", nullable=true)
*/
private $updatedAt;`
AdvertReader.php :
/**
* AdvertReader
*
* #ORM\Table()
* #ORM\Entity
*/
class AdvertReader
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Society\PerfclientBundle\Entity\Advert", inversedBy="readers")
* #ORM\JoinColumn(name="advert_id", referencedColumnName="id")
*/
private $advert;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=255)
*/
private $username;
/**
* #var \DateTime
*
* #ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
/**
* #var \DateTime
*
* #ORM\Column(name="updated_at", type="datetime")
*/
private $updatedAt;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set advert
*
* #param integer $advert
* #return AdvertReader
*/
public function setAdvert($advert)
{
$this->advert = $advert;
return $this;
}
/**
* Get advert
*
* #return integer
*/
public function getAdvert()
{
return $this->advert;
}
/**
* Set username
*
* #param string $username
* #return AdvertReader
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get username
*
* #return string
*/
public function getUsername()
{
return $this->username;
}
And my viewAction()
/**
* #param $id
* #return Response
*/
public function viewAction($id)
{
// On récupère le repository
$repository = $this->getDoctrine()
->getManager()
->getRepository('SocietyPerfclientBundle:Advert')
;
$advert = $repository->find($id);
if (null === $advert) {
throw new NotFoundHttpException("L'annonce d'id ".$id." n'existe pas.");
}
$securityContext = $this->container->get('security.context');
$user = $securityContext->getToken()->getUser();
if ($securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
$em = $this->getDoctrine()->getManager();
$queryBuilder = $em->createQueryBuilder();
$query = $queryBuilder->select('count(ar.id)')
->from('SocietyPerfclientBundle:AdvertReader', 'ar')
->where('ar.advert = :advert')
->andWhere('ar.username = :username')
->setParameter('advert', $advert->getId())
->setParameter('username', $user->getUsername())
->getQuery();
$advertReaderCount = $query->getSingleScalarResult();
if ($advertReaderCount <= 0) {
// l'utilisateur lis pour la 1er fois
$advertReader = new AdvertReader;
$advertReader->setAdvert($advert->getId());
$advertReader->setUpdatedAt(new \DateTime);
$advertReader->setUsername($user->getUsername());
$advertReader->setCreatedAt(new \DateTime);
$em->persist($advertReader);
$em->flush();
}
}
return $this->render('SocietyPerfclientBundle:Default:view.html.twig', array(
'advert' => $advert,
));
}
doctrine:schema:validate -> [Mapping] OK [Database] OK
Error500 ORM Exception : Found entity of type on association Society\PerfclientBundle\Entity\AdvertReader#advert, but expecting Society\PerfclientBundle\Entity\Advert
Stack Trace Profiler :
UnitOfWork ->computeAssociationChanges (array('fieldName' => 'advert', 'joinColumns' => array(array('name' => 'advert_id', 'unique' => false, 'nullable' => true, 'onDelete' => null, 'columnDefinition' => null, 'referencedColumnName' => 'id')), 'cascade' => array(), 'inversedBy' => 'readers', 'targetEntity' => 'Society\PerfclientBundle\Entity\Advert', 'fetch' => '2', 'type' => '2', 'mappedBy' => null, 'isOwningSide' => true, 'sourceEntity' => 'Society\PerfclientBundle\Entity\AdvertReader', 'isCascadeRemove' => false, 'isCascadePersist' => false, 'isCascadeRefresh' => false, 'isCascadeMerge' => false, 'isCascadeDetach' => false, 'sourceToTargetKeyColumns' => array('advert_id' => 'id'), 'joinColumnFieldNames' => array('advert_id' => 'advert_id'), 'targetToSourceKeyColumns' => array('id' => 'advert_id'), 'orphanRemoval' => false), '26')
What's wrong with my entity relation ?
Please help me :)
I think the problem comes from this code piece of code
$advertReader->setAdvert($advert->getId());
Try this instead:
$advertReader->setAdvert($advert);
Doctrine is expecting an object on that relation and instead you pass some integer.
Hope this helps. Happy coding
Alexandru Cosoi

Doctrine cascade persistence never check if object exists Symfony 2

I have a form which save queries. This is organized in the way of a people who ask is saved also this create a threat where is saving the id_question and emailperson. The issue is that the same person does another question. Logically, this report a primary key error:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'drj00003#ujaen.es' for key 'PRIMARY'
How do I fix it? In the constructor of class I cannot check if this exists and all registers are generated automatically in cascade without form.
It should be easy but I can't solve this.
class HiloType extends AbstractType{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('consultanteemail', new ConsultanteType(), array ('label' => false))
->add('personalemail', 'entity', array(
'label' => 'Personal de Soporte',
'class' => 'GuiasDocentes\AppBundle\Entity\Personal',
'property' => 'Enunciado',
'by_reference' => 'false',
'query_builder' => function(PersonalRepository $pr) {
$query= $pr->createQueryBuilder('u')
;
return $query;
},
'empty_value' => 'Elige un perfil de consulta:',
))
;
}
...
class Hilo{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \GuiasDocentes\AppBundle\Entity\Personal
*
* #ORM\ManyToOne(targetEntity="Personal", inversedBy="hilos", cascade ={"ALL"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="personalEmail", referencedColumnName="email")
* })
*/
private $personalemail;
/**
* #var \GuiasDocentes\AppBundle\Entity\Consultante
*
* #ORM\ManyToOne(targetEntity="Consultante", inversedBy="hilos", cascade ={"ALL"} )
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="consultanteEmail", referencedColumnName="email")
* })
*/
private $consultanteemail;
/* Customized code */
/**
* #ORM\OneToMany(targetEntity="GuiasDocentes\AppBundle\Entity\Consulta", mappedBy="hiloid")
* #Assert\Valid()
*/
private $consultas;
public function __construct(){
$this->consultas = new ArrayCollection();
}
public function setConsultas (Consulta $consulta){
$this->hilos[]=$consulta;
}
public function addConsulta (\GuiasDocentes\AppBundle\Entity\Consulta $consulta){
$this->hilos[] = $consulta;
}
/* End customized code */
...
class Consultante{
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=50, nullable=false)
* #ORM\Id
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="nombre", type="string", length=30, nullable=true)
*/
private $nombre;
/**
* #var string
*
* #ORM\Column(name="apellidos", type="string", length=50, nullable=true)
*/
private $apellidos;
/* Customized code */
/**
* #ORM\OneToMany(targetEntity="GuiasDocentes\AppBundle\Entity\Hilo", mappedBy="consultanteemail")
* #Assert\Valid()
*/
private $hilos;
public function __construct(){
$this->hilos = new ArrayCollection();
}
public function setHilos (Hilo $hilo){
$this->hilos[]=$hilo;
}
public function addHilo (\GuiasDocentes\AppBundle\Entity\Hilo $hilo){
$this->hilos[] = $hilo;
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/* End customize code */
...
public function contactoAction (Request $request){
$session = $request->getSession();
$perfil = $session->get('perfil');
$consultaHasAsignatura = new ConsultaHasAsignatura();
$form = $this->createForm(new ConsultaHasAsignaturaType(), $consultaHasAsignatura);
if ($request->isMethod('POST')){
$form->handleRequest($request);
if($form->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($consultaHasAsignatura);
$em->flush();
return $this->redirectToRoute('guias_docentes_app_homepage');
}
}
return $this->render('GuiasDocentesAppBundle:FAQ:contacto.html.twig', array('perfil'=>$perfil,
'form' => $form->createView()
));
}

Sonata Admin sonata_type_collection

I have an issue with Sonata admin form...
I would like to insert music in a album with position...
My issue is when I create an album... I can add many as I want AlbumHasMusic... But when I submit my form... Each AlbumHasMusic have a null album whereas music and positions are ok.
How could I put album id to each AlbumHasMusic ?
These are my Entities :
Album:
/**
* Album
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\AlbumRepository")
* #ORM\HasLifecycleCallbacks()
*/
class Album
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="titre", type="string", length=255)
*/
private $titre;
/**
* #var UploadedFile
*/
private $cover;
/**
* #var String
*
* #ORM\Column(name="filename", type="string", length=255, nullable=true)
*/
private $covername;
/**
*
* #var Array<AlbumHasMusiques>
* #ORM\OneToMany(targetEntity="AppBundle\Entity\AlbumHasMusiques", mappedBy="album", cascade={"persist"})
*/
private $albumHasMusiques;
/**
* #var \DateTime
*
* #ORM\Column(name="updated", type="time", nullable=true)
*/
private $updated;
}
AlbumHasMusic:
/**
* AlbumHasMusiques
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\AlbumHasMusiquesRepository")
*/
class AlbumHasMusiques
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Album
*
* #ORM\ManyToOne(targetEntity="Album")
* #ORM\JoinColumn(name="album_id", referencedColumnName="id")
*/
private $album;
/**
* #var Musique
*
* #ORM\ManyToOne(targetEntity="Musique")
* #ORM\JoinColumn(name="musique_id", referencedColumnName="id")
*/
private $musique;
/**
* #var integer
*
* #ORM\Column(name="position", type="integer")
*/
private $position;
}
Music:
/**
* Musique
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\MusiqueRepository")
* #ORM\HasLifecycleCallbacks()
*/
class Musique
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime
*
* #ORM\Column(name="duree", type="time", nullable=true)
*/
private $duree;
/**
* #var \DateTime
*
* #ORM\Column(name="updated", type="time", nullable=true)
*/
private $updated;
/**
* #var string
*/
private $file;
/**
* #var String
*
* #ORM\Column(name="filename", type="string", length=255, nullable=true)
*/
private $filename;
/**
* #var string
*
* #ORM\Column(name="titre", type="string", length=255, nullable=true)
*/
private $titre;
/**
* #var Genre
*
* #ORM\ManyToOne(targetEntity="Genre", inversedBy="musiques")
* #ORM\JoinColumn(name="genre_id", referencedColumnName="id", nullable=false)
*/
private $genre;
/**
* #ORM\ManyToMany(targetEntity="MotClef", inversedBy="musiques")
*/
private $motsClef;
}
I would like to do something like Galeries and media of Sonata Media Bundle.
And this is my AlbumAdmin:
class AlbumAdmin extends Admin
{
/**
* #param FormMapper $formMapper
*/
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('titre')
->add('covername')
->add('updated')
->add('albumHasMusiques', 'sonata_type_collection', array(
'cascade_validation' => true,
'by_reference' => false,
), array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position',
'link_parameters' => array('context' => 'default'),
'admin_code' => 'app.admin.album_has_musiques',
)
)
;
}
/**
* {#inheritdoc}
*/
public function prePersist($album)
{
$album->setAlbumHasMusiques($album->getAlbumHasMusiques());
}
/**
* {#inheritdoc}
*/
public function preUpdate($album)
{
$album->setAlbumHasMusiques($album->getAlbumHasMusiques());
}
}
And AlbumHasMusicAdmin :
class AlbumHasMusiquesAdmin extends Admin
{
/**
* #param FormMapper $formMapper
*/
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('musique', 'sonata_type_model_list', array('required' => true), array(
'link_parameters' => ['context' => 'default'],
))
->add('position', 'hidden')
;
}
}
Do you have any idea ?
I think that you missed something in the addAlbumHasMusiques() function in your album entity : make sure that you wrote it like that :
public function addAlbumHasMusiques(\AppBundle\Entity\AlbumHasMusiques $albumHasMusiques) {
$albumHasMusiques->setAlbum($this); // The important line !!!!
$this->albumHasMusiques[] = $albumHasMusiques;
return $this;
}

Show only user with a specific role in FormFields sonata

Here I am on a project for my studies and I use symfony fosUser and sonata, I have a Book entity that I administer with sonata.
The problem is that I want to give an user to a book, but it becomes complicated when I want to find in my field user only the user whose role ROLE_MyRole, I search on forums for several hours without finding.
I'm sorry for my incompetence I learn symfony recently and does not yet have enough experience.
This is my BookAdmin
class BookAdmin extends Admin
{
// Fields to be shown on create/edit forms
protected function configureFormFields(FormMapper $formMapper)
{
static $options = array();
$currentBook = $this->getSubject();
if (null !== $currentBook->getFileName())
$options = array(
'required' => false,
'help' => 'getWebPath().'">Download File : '.$currentBook->getFileName().'',
);
$formMapper
->add('title', null, array('label' => 'Titre : '))
->add('summary', null, array('label' => 'Résumé : '))
->add('category', null, array('label' => 'Catégorie : '))
->add('readers', null, array('label' => 'Lecteur(s) : '))
->add('file', 'file', $options)
;
}
And this is my Book entity with readers field
class Book
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
protected $title;
/**
* #var string
*
* #ORM\Column(name="summary", type="text")
*/
protected $summary;
/**
* #Assert\File(
* mimeTypes={"application/pdf", "application/x-pdf"},
* mimeTypesMessage="Only PDF"
* )
*/
protected $file;
/**
* #var string
*
* #ORM\Column(name="file_path", type="string", length=255)
*/
protected $fileName;
/**
* #var \DateTime
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created_at", type="datetime")
*/
protected $createdAt;
/**
* #var \DateTime
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="updated_at", type="datetime")
*/
protected $updatedAt;
/**
* #var bool
*
* #ORM\Column(name="enabled", type="boolean", nullable=true)
*/
protected $enabled;
/**
* #var bool
*
* #ORM\Column(name="received_by_reader", type="boolean", nullable=true)
*/
protected $receivedByReader;
/**
* #var bool
*
* #ORM\Column(name="download_by_reader", type="boolean", nullable=true)
*/
protected $downloadByReader;
/**
* #var bool
*
* #ORM\Column(name="send_by_reader", type="boolean", nullable=true)
*/
protected $sendByReader;
/**
* #var bool
*
* #ORM\Column(name="reader_validation", type="boolean", nullable=true)
*/
protected $readerValidation;
/**
* #var bool
*
* #ORM\Column(name="edited", type="boolean", nullable=true)
*/
protected $edited;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User")
*/
protected $author;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Application\Sonata\UserBundle\Entity\User", cascade={"persist"})
*/
protected $readers;
Thanks You
You can do something like this in your admin class:
$formMapper
->add('readers', null, array(
'label' => 'Lecteur(s) : ',
'code' => 'getReadersWithMyRole'
))
and define in your User entity the following method:
/**
* Returns readers with "ROLE_MyRole" role
*
* #return array
*/
public function getReadersWithMyRole()
{
$result = array();
foreach ($this->getReaders() as $reader) {
if ($reader->hasRole('ROLE_MyRole')) {
$result[] = $reader;
}
}
return $result;
}
Hope it helps.
it doesn't work too but we have find a solution, we can do that like this :
->add('reader', null, array(
'class' => 'ApplicationSonataUserBundle:User',
'label' => 'Lecteur : ',
'query_builder' => function (EntityRepository $er) {
$qb = $er->createQueryBuilder('u');
$qb->where($qb->expr()->like('u.roles', $qb->expr()->literal('%ROLE_READER%')));
return $qb;
}))
if it can help someone

Doctrine OneToOne remove entity

When I remove entity, relations OneToOne has not been removed. Gallery has been removed successfully from database, but largeImage, mediumImage and smallImage not.
What I do wrong?
Delete action in controller:
public function deleteAction($id)
{
$em = $this->getDoctrine()->getManager();
$offer = $em->getRepository('ToCmsBundle:Offer')->find($id);
if($offer) {
if(!$offer->getGallery()->isEmpty()) {
foreach($offer->getGallery() as $key => $image) {
$image->setOffer(null);
$offer->removeGallery($image);
//$em->remove($image);
//$em->flush();
echo $image->getPath() . '<br/>';
}
} else {
echo 'gallery is empty';
}
$offer->setLargeImage(null);
$offer->setMediumImage(null);
$offer->setSmallImage(null);
$em->remove($offer);
$em->flush();
}
return $this->redirect($this->generateUrl('to_offer_list'));
}
My owner class:
/**
* Offer
*
* #ORM\Table(name="to_offer")
* #ORM\HasLifecycleCallbacks
* #ORM\Entity(repositoryClass="To\CmsBundle\Repository\OfferRepository")
*/
class Offer {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="content", type="text")
*/
private $content;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="Image", mappedBy="offer", cascade={"all"}, orphanRemoval=true)
*/
private $gallery;
/**
* #var Image
*
* #ORM\OneToOne(targetEntity="Image", cascade={"all"}, orphanRemoval=true)
*/
public $largeImage;
/**
* #var Image
*
* #ORM\OneToOne(targetEntity="Image", cascade={"all"}, orphanRemoval=true)
*/
public $mediumImage;
/**
* #var Image
*
* #ORM\OneToOne(targetEntity="Image", cascade={"all"}, orphanRemoval=true)
*/
public $smallImage;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Category", inversedBy="articles")
* #ORM\JoinTable(name="to_articles_categories")
*/
private $categories;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="author_id", referencedColumnName="id")
*/
private $author;
public function __construct() {
$this->gallery = new ArrayCollection();
$this->categories = new ArrayCollection();
}
/** other stuff */
}
Image class:
/**
* Image
*
* #ORM\Table(name="to_images")
* #ORM\Entity(repositoryClass="To\CmsBundle\Repository\ImageRepository")
* #ORM\HasLifecycleCallbacks
*/
class Image
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="hashName", type="string", length=255)
*/
private $hashName;
/**
* #var integer
*
* #ORM\Column(name="position", type="integer")
*/
private $position;
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
/**
* #var string
*
* #ORM\Column(name="thumbnail", type="string", length=255)
*/
private $thumbnail;
/**
* #var string
*
* #ORM\Column(name="originalImage", type="string", length=255)
*/
private $originalImage;
/**
* #var Offer
*
* #ORM\ManyToOne(targetEntity="Offer", inversedBy="gallery")
* #ORM\JoinColumn(name="offer_id", referencedColumnName="id", nullable=true)
*/
private $offer;
/**
* #var \DateTime
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="createdAt", type="datetime")
*/
private $createdAt;
/**
* #var \DateTime
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="updatedAt", type="datetime")
*/
private $updatedAt;
/**
* #var integer
*
* #ORM\Column(name="x", type="integer")
*/
protected $x = 0;
/**
* #var integer
*
* #ORM\Column(name="y", type="integer")
*/
protected $y = 0;
/**
* #var integer
*
* #ORM\Column(name="w", type="integer")
*/
protected $w = 0;
/**
* #var integer
*
* #ORM\Column(name="h", type="integer")
*/
protected $h = 0;
/**
* #var boolean
*
* #ORM\Column(name="defaultImage", type="boolean")
*/
private $default = 0;
public function __construct() {
$this->position = 0;
$this->default = 0;
$this->x = 0;
$this->y = 0;
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
$this->position = $this->position ?: 0;
$this->default = $this->default ?: 0;
$this->x = $this->x ?: 0;
$this->y = $this->y ?: 0;
/*if (null !== $this->getFile()) {
// do whatever you want to generate a unique name
$this->name = sha1(uniqid(mt_rand(), true));
$this->originName = $this->getFile()->getClientOriginalName();
$this->extension = $this->getFile()->guessExtension();
$this->path = $this->name.'.'.$this->extension;
}*/
}
/**
* #ORM\PreRemove()
*/
public function removeUpload()
{
echo $this->getRootPath().$this->originalImage; echo '<br/>';
echo $this->getRootPath().$this->thumbnail; echo '<br/>';
echo $this->getRootPath().$this->path; echo '<br/>';
echo '<br/><br/>';
if(file_exists($this->getRootPath().$this->originalImage))
unlink($this->getRootPath().$this->originalImage);
if(file_exists($this->getRootPath().$this->thumbnail))
unlink($this->getRootPath().$this->thumbnail);
if(file_exists($this->getRootPath().$this->path))
unlink($this->getRootPath().$this->path);
}
/** other stuff */
}
Your entities are referencing each other so you should try remove these :
$offer->setLargeImage(null);
$offer->setMediumImage(null);
$offer->setSmallImage(null);
and call flush() once before $em->remove($offer); and once after.
foreach ($offer->getGallery() as $image) {
$offer->removeGallery($image);
}
$em->flush();
$em->remove($offer);
$em->flush();
This will only work as is if all the images are "privately owned", meaning that they are not referenced multiple times in this entity or in another entity.
$offer->setLargeImage(null);
$offer->setMediumImage(null);
$offer->setSmallImage(null);
Your "$offer" doesn't have the images anymore when you're deleting it.
UPDATE
When you are doing your 'find()' you don't have the image object, but a proxy because of the lazy loading. If you need the offer with image objects, create a query with leftJoin on the images to get the offer AND images. And then, I think that you'll not have problems.
(look at the end of this tutorial to have an example of a leftjoin on objects : http://symfony.com/fr/doc/current/cookbook/security/entity_provider.html#gerer-les-roles-via-la-base-de-donnees)

Resources