Why I got NULL instead the related "paises"? - php

I've FabricanteDistribuidor.php entity with this code:
/**
* #ORM\Entity
* #ORM\Table(name="nomencladores.fabricante_distribuidor", schema="nomencladores")
* #ORM\Entity(repositoryClass="AppBundle\Entity\Repository\FabricanteDistribuidorRepository")
* #UniqueEntity(fields={"nombre"}, message="El nombre ya está registrado")
*/
class FabricanteDistribuidor
{
use IdentifierAutogeneratedEntityTrait;
use NamedEntityTrait;
// ... some other fields
/**
* #ORM\ManyToMany(targetEntity="Sencamer\AppBundle\Entity\Pais")
* #ORM\JoinTable(name="negocio.fabricante_distribuidor_pais", schema="negocio",
* joinColumns={#ORM\JoinColumn(name="fabricante_distribuidor", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="pais_id", referencedColumnName="id")}
* )
*/
protected $paises;
/**
* Set paises
*
* #param \AppBundle\Entity\Pais $pais
* #return FabricanteDistribuidor
*/
public function setPaises(\AppBundle\Entity\Pais $pais)
{
$this->paises[] = $pais;
return $this;
}
/**
* Get paises
*
* #return string
*/
public function getPaises()
{
return $this->paises;
}
}
Then in the controller I'm trying to get one records and it associated like in this case will be all the paises as follow:
public function obtenerDetallesFabricanteAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AppBundle:FabricanteDistribuidor')->find($request->query->get('id'));
if ($request->isXmlHttpRequest()) {
$response['entities'] = array();
$dataResponse = array();
// ... some other fields
$dataResponse['paises'] = $entity->getPaises();
$response['entities'][] = $dataResponse;
return new JsonResponse($response);
}
}
In the JSON response I get everything fine but paises is set to NULL and the relation table fabricante_distribuidor_pais has value for the fabricante I'm seek, why? What I'm doing wrong in the ManyToMany relationship?
I watch in dev.log and join is never made:
doctrine.DEBUG: SELECT t0.direccion AS direccion1, t0.telefono AS
telefono2, t0.fax AS fax3, t0.correo AS correo4, t0.id AS id5,
t0.nombre AS nombre6 FROM nomencladores.fabricante_distribuidor t0
WHERE t0.id = ? ["1"] []
Why?
Solution and some concerns around it
After read and read and do a intensive research through Stackoverflow, Google and so on I get the solution, it working, I do not know if is the best of if it's right so you tell me:
FabricanteDistribuidor.php
class FabricanteDistribuidor
{
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Pais", mappedBy="fabricanteDistribuidor", cascade={"persist"})
*/
private $paises;
/**
* Set paises
*
* #param \AppBundle\Entity\Pais $pais
* #return FabricanteDistribuidor
*/
public function setPaises(\Sencamer\AppBundle\Entity\Pais $pais)
{
$this->paises[] = $pais;
return $this;
}
/**
* Get paises
*
* #return Doctrine\Common\Collections\Collection
*/
public function getPaises()
{
return $this->paises;
}
}
Pais.php
class Pais
{
use IdentifierAutogeneratedEntityTrait;
use NamedEntityTrait;
use ActiveEntityTrait;
/**
* #ORM\ManyToMany(targetEntity="Sencamer\AppBundle\Entity\FabricanteDistribuidor", inversedBy="paises", cascade={"persist"})
* #ORM\JoinTable(name="negocio.fabricante_distribuidor_pais", schema="negocio",
* joinColumns={#ORM\JoinColumn(name="fabricante_distribuidor", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="pais_id", referencedColumnName="id")}
* )
*/
protected $fabricanteDistribuidor;
/**
* Add fabricanteDistribuidor
*
* #param AppBundle\Entity\FabricanteDistribuidor $fabricanteDistribuidor
*/
public function addfabricanteDistribuidor(\AppBundle\Entity\FabricanteDistribuidor $fabricanteDistribuidor)
{
$this->fabricanteDistribuidor[] = $fabricanteDistribuidor;
}
/**
* Get fabricanteDistribuidor
*
* #return Doctrine\Common\Collections\Collection
*/
public function getfabricanteDistribuidor()
{
return $this->fabricanteDistribuidor;
}
}
Then in my controller I iterate over the request looking for each pais I want to add and flush it when the object is persisted:
if ($fabricanteDistribuidorForm->isValid()) {
try {
$em->persist($fabricanteDistribuidorEntity);
$em->flush();
$formDataPais = $request->get('fabricanteDistribuidor')['pais'];
foreach ($formDataPais as $paisId) {
$pais = $em->getRepository('AppBundle:Pais')->find($paisId);
$fabricanteDistribuidorEntity->setPaises($pais);
$em->flush();
}
$response['entities'][] = $dataResponse;
} catch (Exception $ex) {
$response['success'] = FALSE;
$response['error'] = $ex->getMessage();
}
} else {
return $this->getFormErrors($fabricanteDistribuidorForm); ;
}
That way all works fine and data is persisted in the right way. Now around this solution I have another issue and a concern. The issue is that I'm trying to get now the related paises from FabricanteDistribuidoras follow and I'm doing something wrong since I can't get their names, so what is wrong in my code?
public function obtenerDetallesFabricanteAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AppBundle:FabricanteDistribuidor')->find($request->query->get('id'));
if ($request->isXmlHttpRequest()) {
$response['entities'] = array();
$dataResponse = array();
// rest of columns ....
if ($entity->getPaises() instanceof Pais) {
$paises = array();
foreach ($entity->getPaises() as $pais) {
$paises[] = $pais->getNombre();
}
$dataResponse['paises'] = $paises;
}
$response['entities'][] = $dataResponse;
return new JsonResponse($response);
}
}
The concern is around the Pais class as you notice I added the inversed side $fabricanteDistribuidor so, do I have to insert this any time I want to insert a new Pais or is just to tell Doctrine how to deal with proxies inside it? I've not clear yet how owning/inversed side works yet maybe due to this I did thing as my code shown. Any advice around this too?

my n-m realtions are like this:
/**
* #ORM\ManyToMany(targetEntity="Sencamer\AppBundle\Entity\Pais")
* #ORM\JoinTable(name="negocio.fabricante_distribuidor_pais", schema="negocio",
* joinColumns={#ORM\JoinColumn(name="fabricante_distribuidor_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="pais_id", referencedColumnName="id")}
* )
*/
check if in your join sentence, the "fabricante_distribuidor" is a "id".
And remember, you need to put in your constructor to set the "paises" like arrayCollection:
public function __construct() {
$this->paises = new \Doctrine\Common\Collections\ArrayCollection();
}
and in the n-m relationship is a good practice create addPaises and not setPaises:
public function addPais(\AppBundle\Entity\Pais $pais){
$this->paises[] = $pais;
return $this;
}
I think, somewhere in your code you add the "paises" to your "fabricante.distribuidor", isn't it?
I hope that helps you

Related

foreign key null in collection form symfony 3

i have two entities Survey.php and Choice.php I create a form to add new survey and multi choices, I used a many-to-one relation between the two entities, the problem is when I submit for a new survey, the foreign key of choice entity return null
here's my code
Survey.PHP
/**
* Survey
*
* #ORM\Table(name="survey")
* #ORM\Entity(repositoryClass="AppBundle\Repository\SurveyRepository")
*/
class Survey
{
/....
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Choice", mappedBy="survey_id",cascade="persist")
* #ORM\JoinColumn(nullable=false, referencedColumnName="id")
*/
private $choice;
public function __construct()
{
$this->choice = new ArrayCollection();
}
/**
* Add choice
*
* #param \AppBundle\Entity\Choice $choice
*
* #return Survey
*/
public function addChoice(\AppBundle\Entity\Choice $choice)
{
$this->choice[] = $choice;
$choice->setSurvey($this);
return $this;
}
/**
* Remove choice
*
* #param \AppBundle\Entity\Choice $choice
*/
public function removeChoice(\AppBundle\Entity\Choice $choice)
{
$this->choice->removeElement($choice);
}
/**
* Get choice
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getChoice()
{
return $this->choice;
}
}
Choice.php
/**
* Choice
* #ORM\Table(name="choice")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ChoiceRepository")
*/
class Choice
{
/**
* #var int
* #ORM\ManyToOne(targetEntity="Survey",inversedBy="choice")
*/
private $survey;
/**
* Set survey
*
* #param \AppBundle\Entity\Survey $survey
*
* #return Choice
*/
public function setSurveyId(\AppBundle\Entity\Survey $survey)
{
$this->survey = $survey;
return $this;
}
/**
* Get surveyId
*
* #return \AppBundle\Entity\Survey
*/
public function getSurveyId()
{
return $this->survey_id;
}
}
SurveyController.php
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Survey;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
/**
* Survey controller.
*
*/
class SurveyController extends Controller
{
/**
* Creates a new survey entity.
* #param Request $request
* #return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function newAction(Request $request)
{
$survey = new Survey();
//
$form = $this->createForm('AppBundle\Form\SurveyType', $survey);
$form->handleRequest($request);
$survey->setCreateDate(new \DateTime('NOW'));
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($survey);
$em->flush();
return $this->redirectToRoute('survey_show', ['id' => $survey->getId()]);
}
return $this->render('survey/new.html.twig', [
'survey' => $survey,
'form' => $form->createView(),
]);
}
any suggestion, btw I think the problem is in the getters and setters )
Link the choice to the survey:
// Survey
public function addChoice(\AppBundle\Entity\Choice $choice)
{
$this->choice[] = $choice;
$choice->setSurvey($this);
return $this;
}
And change the survey_id stuff to survey. Dealing with objects not ids. And of course Survey::choice should be Survey::choices. The name changes might seem minor but will make your easier to maintain.
I fixed the problem by adding a for each loop inside the SurveyController.php
and it works just fine
SurveyController.php
if ($form->isSubmitted() && $form->isValid())
{
foreach ($survey->getChoices() as $choice){
$choice->setSurvey($survey);
}
$em = $this->getDoctrine()->getManager();
$em->persist($survey);
$em->flush();
not "THE best solution" but it gets the job done
It worked with me, but I had to remove the explicit foreign key mapping with the "inversedBy" setting from the class definition. I use a composite foreign key (using two columns), which maybe makes things harder though...

Error while inserting into db using doctrine ORM

I am trying to insert data into db ,but it shows some error like this
My model Entity
Request.php
is here `<?php
namespace EvolisClientRequest\Model\Entities;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Request
{
/**
* #var \Ramsey\Uuid\Uuid
* #ORM\Id
* #ORM\Column(type="uuid")
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="Salesperson", inversedBy="request")
* #ORM\JoinTable(name="request_salesperson")
* #var Salesperson
*/
private $salesperson;
/**
* #ORM\ManyToOne(targetEntity="Client", inversedBy="request")
* #var Client
*/
private $client;
/**
* #ORM\ManyToMany(targetEntity="Status", inversedBy="request")
* #ORM\JoinTable(name="request_status")
* #var Status
*/
private $status;
/**
* #ORM\Column(type="integer")
* #var Qualification
*/
private $qualification;
/**
* #return \Ramsey\Uuid\Uuid
*/
public function getId()
{
return $this->id;
}
/**
* #return Salesperson
*/
public function getSalesperson()
{
return $this->salesperson;
}
/**
* #param Salesperson $salesperson
*/
public function setSalesperson($salesperson)
{
$this->salesperson = $salesperson;
}
/**
* #return Client
*/
public function getClient()
{
return $this->client;
}
/**
* #param Client $client
*/
public function setClient($client)
{
$this->client = $client;
}
/**
* #return Status
*/
public function getStatus()
{
return $this->status;
}
/**
* #param Status $status
*/
public function setStatus($status)
{
$this->status = $status;
}
/**
* #return Qualification
*/
public function getQualification()
{
return $this->qualification;
}
/**
* #param Qualification $qualification
*/
public function setQualification($qualification)
{
$this->qualification = $qualification;
}
public function __construct($salesperson, $client, $status, $qualification) {
$this->salesperson = $salesperson;
$this->client = $client;
$this->status = $status;
$this->qualification = $qualification;
}
}`
Also my
DAO "RequestBaseDao.php" is here,which is automatically generated.
<?php
/*
* This file has been automatically generated by Mouf/ORM.
* DO NOT edit this file, as it might be overwritten.
* If you need to perform changes, edit the RequestDao class instead!
*/
namespace EvolisClientRequest\Model\DAOs;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NonUniqueResultException;
use Mouf\Doctrine\ORM\Event\SaveListenerInterface;
use EvolisClientRequest\Model\Entities\Request;
/**
* The RequestBaseDao class will maintain the persistence of Request class into the request table.
*
* #method Request findByQualification($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByQualification($fieldValue, $orderBy = null)
* #method Request findBySurfaceMin($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneBySurfaceMin($fieldValue, $orderBy = null)
* #method Request findBySurfaceMax($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneBySurfaceMax($fieldValue, $orderBy = null)
* #method Request findByPriceMin($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByPriceMin($fieldValue, $orderBy = null)
* #method Request findByPriceMax($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByPriceMax($fieldValue, $orderBy = null)
* #method Request findByRequestDate($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByRequestDate($fieldValue, $orderBy = null)
*/
class RequestBaseDao extends EntityRepository
{
/**
* #var SaveListenerInterface[]
*/
private $saveListenerCollection;
/**
* #param EntityManagerInterface $entityManager
* #param SaveListenerInterface[] $saveListenerCollection
*/
public function __construct(EntityManagerInterface $entityManager, array $saveListenerCollection = [])
{
parent::__construct($entityManager, $entityManager->getClassMetadata('EvolisClientRequest\Model\Entities\Request'));
$this->saveListenerCollection = $saveListenerCollection;
}
/**
* Get a new persistent entity
* #param ...$params
* #return Request
*/
public function create(...$params) : Request
{
$entity = new Request(...$params);
$this->getEntityManager()->persist($entity);
return $entity;
}
/**
* Peforms a flush on the entity.
*
* #param Request
* #throws \Exception
*/
public function save(Request $entity)
{
foreach ($this->saveListenerCollection as $saveListener) {
$saveListener->preSave($entity);
}
$this->getEntityManager()->flush($entity);
foreach ($this->saveListenerCollection as $saveListener) {
$saveListener->postSave($entity);
}
}
/**
* Peforms remove on the entity.
*
* #param Request $entity
*/
public function remove(Request $entity)
{
$this->getEntityManager()->remove($entity);
}
/**
* Finds only one entity. The criteria must contain all the elements needed to find a unique entity.
* Throw an exception if more than one entity was found.
*
* #param array $criteria
*
* #return Request
*/
public function findUniqueBy(array $criteria) : Request
{
$result = $this->findBy($criteria);
if (count($result) === 1) {
return $result[0];
} elseif (count($result) > 1) {
throw new NonUniqueResultException('More than one Request was found');
} else {
return;
}
}
/**
* Finds only one entity by Qualification.
* Throw an exception if more than one entity was found.
*
* #param mixed $fieldValue the value of the filtered field
*
* #return Request
*/
public function findUniqueByQualification($fieldValue)
{
return $this->findUniqueBy(array('qualification' => $fieldValue));
}
}
My RequestDao.php where i can write queries.
<?php
namespace EvolisClientRequest\Model\DAOs;
use EvolisClientRequest\Model\Entities\Request;
/**
* The RequestDao class will maintain the persistence of Request class into the request table.
*/
class RequestDao extends RequestBaseDao {
/*** PUT YOUR SPECIFIC QUERIES HERE !! ***/
public function setdata()
{
/*$product = new Request();
$product->setStatus('Keyboard');
$product->setClient('000000001ae10dda000000003c4667a6');
$product->setSalesperson('Ergonomic and stylish!');
$product->setQualification('1111');
//var_dump($r);die();
$em = $this->getEntityManager();
$em->persist($product);
$em->flush();*/
$product= $this->create('Keyboard','000000001ae10dda000000003c4667a6','Ergonomic and stylish!','1111');
$this->save($product);
}
}
Finally my Controller "ContactController.php"
<?php
namespace EvolisClientRequest\Controllers;
use EvolisClientRequest\Model\DAOs\ClientDao;
use EvolisClientRequest\Model\Entities\Client;
use EvolisClientRequest\Model\Entities\Clients;
use EvolisClientRequest\Model\DAOs\RequestDao;
use EvolisClientRequest\Model\Entities\Request;
use EvolisClientRequest\Model\Entities\Requests;
use EvolisClientRequest\Model\DAOs\SalespersonDao;
use EvolisClientRequest\Model\Entities\Salesperson;
use EvolisClientRequest\Model\Entities\Salespersons;
use Mouf\Mvc\Splash\Annotations\Get;
use Mouf\Mvc\Splash\Annotations\Post;
use Mouf\Mvc\Splash\Annotations\Put;
use Mouf\Mvc\Splash\Annotations\Delete;
use Mouf\Mvc\Splash\Annotations\URL;
use Mouf\Html\Template\TemplateInterface;
use Mouf\Html\HtmlElement\HtmlBlock;
use Psr\Log\LoggerInterface;
use \Twig_Environment;
use Mouf\Html\Renderer\Twig\TwigTemplate;
use Mouf\Mvc\Splash\HtmlResponse;
use Doctrine\DBAL\DriverManager;
use Zend\Diactoros\Response\JsonResponse;
use Doctrine\Common\Collections\ArrayCollection;
/**
* TODO: write controller comment
*/
class ContactController {
/**
* The logger used by this controller.
* #var LoggerInterface
*/
private $logger;
/**
* The template used by this controller.
* #var TemplateInterface
*/
private $template;
/**
* The header of the page.
* #var HtmlBlock
*/
private $header;
/**
* The main content block of the page.
* #var HtmlBlock
*/
private $content;
/**
* The Twig environment (used to render Twig templates).
* #var Twig_Environment
*/
private $twig;
/**
* Controller's constructor.
* #param LoggerInterface $logger The logger
* #param TemplateInterface $template The template used by this controller
* #param HtmlBlock $content The main content block of the page
* #param Twig_Environment $twig The Twig environment (used to render Twig templates)
*/
public function __construct(LoggerInterface $logger, TemplateInterface $template, HtmlBlock $content, HtmlBlock $header, Twig_Environment $twig, ClientDao $clientDao, RequestDao $requestDao, SalespersonDao $salespersonDao) {
$this->logger = $logger;
$this->template = $template;
$this->content = $content;
$this->twig = $twig;
$this->header = $header;
$this->clientDao = $clientDao;
$this->requestDao = $requestDao;
$this->salespersonDao = $salespersonDao;
}
/**
* #URL("new.html")
*/
public function new() {
// TODO: write content of action here
// Let's add the twig file to the template.
$this->content->addHtmlElement(new TwigTemplate($this->twig, 'views/contact/new.twig', array("message"=>"world")));
$this->header->addHtmlElement(new TwigTemplate($this->twig, 'views/root/header.twig', array("message"=>"world")));
return new HtmlResponse($this->template);
}
/**
* #URL("saveData")
* For Saving the data
*/
public function saveData()
{
/*$newClient = $this->clientDao->create('hello', 'sarathchandran#122.com','8907263949');
$this->clientDao->save($newClient);*/
//$data = array();
//$data['salespersonDao']['salesperson'] = 'example#sales.com';
//$data['request']['qualification'] = 'abcdefgh';
//$newClient = $this->requestDao->create($data);
//$newClient = $this->requestDao->setQualification('Keyboard');
// $this->requestDao->save($newClient);
$user_data=$this->requestDao->setdata();
//return new JsonResponse([ "status"=>0 ]);
}
}
I am using Mouf framework.I am stuck with this problem.Someone Please help me to solve this problem.
Thanks in advance
As advised by #rokas, you should really start reading more about Doctrine. This is not a Mouf issue, this is clearly a Doctrine ORM issue so the appropriate doc is here: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html
Here are a few tips:
Your controller calls the setdata method
$user_data=$this->requestDao->setdata();
The setdata method calls:
$product= $this->create('Keyboard','000000001ae10dda000000003c4667a6','Ergonomic and stylish!','1111');
Now, the create method is:
public function create(...$params) : Request
{
$entity = new Request(...$params);
$this->getEntityManager()->persist($entity);
return $entity;
}
This means that it will call the Request constructor with this parameters:
$entity = new Request('Keyboard','000000001ae10dda000000003c4667a6','Ergonomic and stylish!','1111');
Have a look at the Request constructor:
public function __construct($salesperson, $client, $status, $qualification) {
$this->salesperson = $salesperson;
$this->client = $client;
$this->status = $status;
$this->qualification = $qualification;
}
As you can see, the first parameter is $salesperson. You try to put the value "Keyboard" here. The $salesperson attribute is defined this way:
/**
* #ORM\ManyToMany(targetEntity="Salesperson", inversedBy="request")
* #ORM\JoinTable(name="request_salesperson")
* #var Salesperson
*/
private $salesperson;
/**
* #param Salesperson $salesperson
*/
public function setSalesperson($salesperson)
{
$this->salesperson = $salesperson;
}
Now, here is your problem I think.
The $salesperson property is defined as a "ManyToMany" association. So you really cannot put a string in here, it is a collection of Salesperson. By the way, you should not "set" anything either. The setter should be completely removed.
Instead, you should consider using it as per the documentation here: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html
For instance:
$request->getSalesPersons()->add($someObjectRepresentingAPerson);
Also, notice that since the $salesperson represents a collection of Salesperson object, your should really name it $salesPersons (plural form)

Doctrine2 - Trigger event on property change (PropertyChangeListener)

I am not writing "what did I try" or "what is not working" since I can think of many ways to implement something like this. But I cannot believe that no one did something similar before and that is why I would like to ask the question to see what kind of Doctrine2 best practices show up.
What I want is to trigger an event on a property change. So let's say I have an entity with an $active property and I want a EntityBecameActive event to fire for each entity when the property changes from false to true.
Other libraries often have a PropertyChanged event but there is no such thing available in Doctrine2.
So I have some entity like this:
<?php
namespace Application\Entity;
class Entity
{
/**
* #var int
* #ORM\Id
* #ORM\Column(type="integer");
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var boolean
* #ORM\Column(type="boolean", nullable=false)
*/
protected $active = false;
/**
* Get active.
*
* #return string
*/
public function getActive()
{
return $this->active;
}
/**
* Is active.
*
* #return string
*/
public function isActive()
{
return $this->active;
}
/**
* Set active.
*
* #param bool $active
* #return self
*/
public function setActive($active)
{
$this->active = $active;
return $this;
}
}
Maybe ChangeTracking Policy is what you want, maybe it is not!
The NOTIFY policy is based on the assumption that the entities notify
interested listeners of changes to their properties. For that purpose,
a class that wants to use this policy needs to implement the
NotifyPropertyChanged interface from the Doctrine\Common namespace.
Check full example in link above.
class MyEntity extends DomainObject
{
private $data;
// ... other fields as usual
public function setData($data) {
if ($data != $this->data) { // check: is it actually modified?
$this->onPropertyChanged('data', $this->data, $data);
$this->data = $data;
}
}
}
UPDATE
This is a full example but silly one so you can work on it as you wish. It just demonstrates how you do it, so don't take it too serious!
entity
namespace Football\TeamBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="country")
*/
class Country extends DomainObject
{
/**
* #var int
*
* #ORM\Id
* #ORM\Column(type="smallint")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(type="string", length=2, unique=true)
*/
protected $code;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set code
*
* #param string $code
* #return Country
*/
public function setCode($code)
{
if ($code != $this->code) {
$this->onPropertyChanged('code', $this->code, $code);
$this->code = $code;
}
return $this;
}
/**
* Get code
*
* #return string
*/
public function getCode()
{
return $this->code;
}
}
domainobject
namespace Football\TeamBundle\Entity;
use Doctrine\Common\NotifyPropertyChanged;
use Doctrine\Common\PropertyChangedListener;
abstract class DomainObject implements NotifyPropertyChanged
{
private $listeners = array();
public function addPropertyChangedListener(PropertyChangedListener $listener)
{
$this->listeners[] = $listener;
}
protected function onPropertyChanged($propName, $oldValue, $newValue)
{
$filename = '../src/Football/TeamBundle/Entity/log.txt';
$content = file_get_contents($filename);
if ($this->listeners) {
foreach ($this->listeners as $listener) {
$listener->propertyChanged($this, $propName, $oldValue, $newValue);
file_put_contents($filename, $content . "\n" . time());
}
}
}
}
controller
namespace Football\TeamBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Football\TeamBundle\Entity\Country;
class DefaultController extends Controller
{
public function indexAction()
{
// First run this to create or just manually punt in DB
$this->createAction('AB');
// Run this to update it
$this->updateAction('AB');
return $this->render('FootballTeamBundle:Default:index.html.twig', array('name' => 'inanzzz'));
}
public function createAction($code)
{
$em = $this->getDoctrine()->getManager();
$country = new Country();
$country->setCode($code);
$em->persist($country);
$em->flush();
}
public function updateAction($code)
{
$repo = $this->getDoctrine()->getRepository('FootballTeamBundle:Country');
$country = $repo->findOneBy(array('code' => $code));
$country->setCode('BB');
$em = $this->getDoctrine()->getManager();
$em->flush();
}
}
And have this file with 777 permissions (again, this is test) to it: src/Football/TeamBundle/Entity/log.txt
When you run the code, your log file will have timestamp stored in it, just for demonstration purposes.

How to perform nested reference query in doctrine mongodb

This describes my current schema:
/**
* #MongoDB\Document(repositoryClass="St\AppBundle\Repository\TaxiStateRepository", requireIndexes=true)
* #MongoDB\Index(keys={"location"="2d"})
*/
class TaxiState
{
/**
* #MongoDB\ReferenceOne(targetDocument="Taxi", simple=true, inversedBy="taxiState")
* #MongoDB\Index
*/
protected $taxi;
..
}
/**
* #MongoDB\Document(repositoryClass="St\AppBundle\Repository\TaxiRepository", requireIndexes=true)
*/
class Taxi
{
/**
* #MongoDB\ReferenceOne(targetDocument="Driver", simple=true)
* #MongoDB\Index
*/
protected $driver;
..
}
/**
* #MongoDB\Document(repositoryClass="St\AppBundle\Repository\DriverRepository", requireIndexes=true)
*/
class Driver
{
/**
* #MongoDB\EmbedOne(targetDocument="DriverAccount")
* #MongoDB\Index
*/
protected $driverAccount;
..
}
/** #MongoDB\EmbeddedDocument */
class DriverAccount
{
/**
* #MongoDB\String
* #Assert\NotBlank()
* #Assert\Choice(choices = {"enabled","disabled"}, message="please chose a valid status"); * #MongoDB\Index
*/
protected $status;
I basically want to run a query that filters out disabled driver accounts.. something like this:
return $this->createQueryBuilder()
->field('taxi.driver.driverAccount.status')->equals("enabled")
->getQuery()
->getSingleResult();
it complains that it doesn't have an index taxi.driver etc.. I spent all day looking at by directional reference documentation in doctrine but the examples are so sparse.. help?
For reference.. this was the query that worked right before i introduced that crazy line:
return $this->createQueryBuilder()
->field('status')->equals('available')
->field('taxi')->notIn($taxiObj)
->field('location')->near((float)$location->getLongitude(), (float)$location->getLatitude())
->distanceMultiplier(self::EARTH_RADIUS_KM)
->maxDistance($radius/111.12)
->getQuery()
->execute();
Just in case you were wondering how I "resolved" this (it's a very hacky answer.. but oh well you do what you gotta do right?) this is what I got:
/**
* Find near enabled taxi without rejected request taxi
*
* #param Document\Location $location
* #param int $radius
* #param array $taxis
* #return Document\TaxiState
*/
public function findEnabledNearTaxiWithoutRejectRequest(Document\Location $location, $radius = self::SEARCH_RADIUS, $taxis = array(), $logger)
{
$taxiObj = array_map(function ($item) {
return new \MongoId($item);
}, $taxis);
//ST-135 change to near, spherical, distanceMultiplier and maxDistance in KM
$allTaxiStates = $this->createQueryBuilder()
->field('status')->equals('available')
->field('taxi')->notIn($taxiObj)
->field('location')->near((float)$location->getLongitude(), (float)$location->getLatitude())
->distanceMultiplier(self::EARTH_RADIUS_KM)
->maxDistance($radius/111.12)
->getQuery()
->execute();
$this->addIdsOfDisabledTaxiStates($taxiObj, $allTaxiStates);
if (count($taxiObj) > 0) {
$logger->info("There are ".count($taxiObj)." taxis excluded while looking for taxis to respond to a requst: ".$this->getMongoIdsStr($taxiObj));
}
return $this->createQueryBuilder()
->field('status')->equals('available')
->field('taxi')->notIn($taxiObj)
->field('location')->near((float)$location->getLongitude(), (float)$location->getLatitude())
->distanceMultiplier(self::EARTH_RADIUS_KM)
->maxDistance($radius/111.12)
->getQuery()
->getSingleResult();
}
/**
* Get the Mongo Ids of disabled taxi States
*
* #param $ids existing array of ids we want to append to (passed by reference)
* #param $taxiStates array of Document\TaxiState
* #return array of MongoIds of disabled taxi states
* #author Abdullah
*/
private function addIdsOfDisabledTaxiStates(&$ids, $taxiStates)
{
foreach ($taxiStates as $taxiState) {
if ($taxiState->getTaxi()->getDriver()->getDriverAccount()->getStatus() != DriverAccountModel::STATUS_ENABLED) {
$mongoId = new \MongoId($taxiState->getTaxi()->getId());
array_push($ids, $mongoId);
}
}
return $ids;
}

Symfony2 - Overriding default Doctrine query when working with entities

For my project, I have a workspace (kind of a user) with many projects and I am wondering if there is a way to override the default Doctrine query for when I call $workspace->getProjects() to only fetch the active projects only (not the archived one). This way I won't have to filter my collection and it will reduce the size of the returned data from the database.
/**
* Acme\DemoBundle\Entity\Workspace
*
* #ORM\Table()
* #ORM\Entity
*/
class Workspace {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
private $id;
/**
* #var ArrayCollection $projects
*
* #ORM\OneToMany(targetEntity="Project", mappedBy="workspace")
*/
private $projects;
/**
* Add projects
*
* #param Project $projects
* #return Workspace
*/
public function addProject( Project $projects ) {
$this->projects[] = $projects;
return $this;
}
/**
* Remove projects
*
* #param Project $projects
*/
public function removeProject( Project $projects ) {
$this->projects->removeElement( $projects );
}
/**
* Get projects
*
* #return Collection
*/
public function getProjects() {
return $this->projects;
}
You will have to write your own method in the Entity Repository Class.
http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes
To do that, you can use criteria.
Here is an exemple from the doctrine doc, to adapt to your needs
use Doctrine\Common\Collections\Criteria;
$criteria = Criteria::create()
->where(Criteria::expr()->eq("birthday", "1982-02-17"))
->orderBy(array("username" => Criteria::ASC))
->setFirstResult(0)
->setMaxResults(20)
;
$birthdayUsers = $object-getUsers()->matching($criteria);
You can create some listener, and catch doctrine events: http://docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/event-listeners.html
And modify your query before execute as you wish;
Example from Doctrine docs, for custom hook on delete:
<?php
class UserListener extends Doctrine_EventListener
{
/**
* Skip the normal delete options so we can override it with our own
*
* #param Doctrine_Event $event
* #return void
*/
public function preDelete( Doctrine_Event $event )
{
$event->skipOperation();
}
/**
* Implement postDelete() hook and set the deleted flag to true
*
* #param Doctrine_Event $event
* #return void
*/
public function postDelete( Doctrine_Event $event )
{
$name = $this->_options['name'];
$event->getInvoker()->$name = true;
$event->getInvoker()->save();
}
/**
* Implement preDqlDelete() hook and modify a dql delete query so it updates the deleted flag
* instead of deleting the record
*
* #param Doctrine_Event $event
* #return void
*/
public function preDqlDelete( Doctrine_Event $event )
{
$params = $event->getParams();
$field = $params['alias'] . '.deleted';
$q = $event->getQuery();
if ( ! $q->contains( $field ) )
{
$q->from('')->update( $params['component'] . ' ' . $params['alias'] );
$q->set( $field, '?', array(false) );
$q->addWhere( $field . ' = ?', array(true) );
}
}
/**
* Implement preDqlDelete() hook and add the deleted flag to all queries for which this model
* is being used in.
*
* #param Doctrine_Event $event
* #return void
*/
public function preDqlSelect( Doctrine_Event $event )
{
$params = $event->getParams();
$field = $params['alias'] . '.deleted';
$q = $event->getQuery();
if ( ! $q->contains( $field ) )
{
$q->addWhere( $field . ' = ?', array(false) );
}
}
}
I think you can put your logic inside the getter method itself
public function getProjects() {
$filtered_projects = array()
foreach($this->projects as $project)
{
// Your logic here
// e.g.
// if($project->getIsActive()){
// $filtered_projects[] = $project;
// }
}
return $filtered_projects;
}

Categories