I am creating small application with just two entities, Order and Shipment.
The Shipment entity is as follows: (methods removed to keep it short)
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $username
*
* #ORM\Column(name="username", type="string", length=255)
*/
private $username;
/**
* #var string $password
*
* #ORM\Column(name="password", type="string", length=255)
*/
private $password;
/**
* #var integer $order_id
*
* #ORM\Column(name="order_id", type="integer")
*/
private $order_id;
/**
* #var smallint $payment_type
*
* #ORM\Column(name="payment_type", type="smallint")
*/
private $payment_type;
In my controller I am trying to query using the order_id but my findOneByOrderId method is not working.
$orderExists = $this->getDoctrine()
->getRepository('ShipBundle:Shipment')
->findOneByOrderId($orderId);
var_dump($orderExists); die();
The error I get is:
Entity 'ShipBundle\Entity\Shipment' has no field 'orderId'. You can therefore not call 'findOneByOrderId' on the entities' repository.
If I am not wrong, Doctrine find methods join the variables at underscores and capitalize them. What am I doing wrong?
I managed to solve the problem with the hint from pomaxa and Doctrine2 documentation.
The correct code would be:
$orderExists = $this->getDoctrine()
->getRepository('ShipBundle:Shipment')
->findOneBy(array('order_id' => $orderId));
explained at: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#by-simple-conditions
Thanks everyone for the help. I appreciate it.
You could use the inbuilt relationship capabilities of Doctrine2 instead of using an id of order in your entity Shipment manually
That way you would have a relationship Doctrine is aware of.
$orders = $shipment->getOrders();
Look here: http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/association-mapping.html
Problem in this line
private $order_id;
Use it
private $orderId;
It is ok. For db you will have order_id.
Just to clarify, the reason for the error was that you needed to pass an Array into the findOneBy();
This is wrong: , ->findOneByOrderId($orderId); in
$orderExists = $this->getDoctrine()
->getRepository('ShipBundle:Shipment')
->findOneByOrderId($orderId);
An array must be passed. array('order_id' => $orderId)
$orderExists = $this->getDoctrine()
->getRepository('ShipBundle:Shipment')
->findOneBy(array('order_id' => $orderId));
OR SHORTHAND ['order_id'=> $orderId] as long as you are in PHP >= 5.4
$orderExists = $this->getDoctrine()
->getRepository('ShipBundle:Shipment')
->findOneBy(['order_id'=> $orderId]);
Related
As I cannot post the real code here, im using a substitute, that basically is the same, so please dont wonder if you find syntactic errors.
I have the following setup:
PHP 7.0,Symphony,Doctrine which is working with a MySQL database.
The classes are as following:
/*
* #ORM\Table(name="Postoffice")
* #ORM\Entity(repositoryClass="\AppBundle\Repsoitory\PostOfficeRepository")
*/
class Postoffice
{
/**
* Holds the Doctrine entity manager for database interaction
* #var EntityManager $em
*/
protected $em;
/**
* #var int
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(stragegy="AUTO")
*/
private $id;
/** #var ArrayCollection
* #ORM\ManyToMany(targetEntity="MailBox")
* #ORM\JoinTable(name="PostOfficeToMailBoxMapping",
* joinColumns={#ORM\JoinColumn(name="PostOfficeId",referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="MailBoxId",referencedColumnName="id",unique=true)})
*/
private $packets;
public function __construct(EntityManager $em)
{
$this->$packets = new ArrayCollection();
$this->em = $em;
}
}
/*
* #ORM\Table(name="PostStorage")
* #ORM\Entity(repositoryClass="\AppBundle\Repsoitory\PoststorageRepository")
*/
class Poststorage
{
/**
* Holds the Doctrine entity manager for database interaction
* #var EntityManager $em
*/
protected $em;
/**
* #var int
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(stragegy="AUTO")
*/
private $id;
/** #var ArrayCollection
* #ORM\ManyToMany(targetEntity="MailBox")
* #ORM\JoinTable(name="PostStorageToMailBoxesMapping",
* joinColumns={#ORM\JoinColumn(name="PoststorageId",referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="MailBoxId",referencedColumnName="id",unique=true)})
*/
private $MailBoxes;
public function __construct(EntityManager $em)
{
$this->MailBoxes = new ArrayCollection();
$this->em = $em;
}
public function delete()
{
//remove each box on its own
foreach ($this->Mailboxes as $iterMailBox)
$this->em->remove($iterMailBox);
$this->em->remove($this);
$this->em->flush();
}
}
/*
* #ORM\Table(name="MailBox")
* #ORM\Entity(repositoryClass="\AppBundle\Repsoitory\MailBoxRepository")
*/
class MailBox
{
/**
* #var int
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(stragegy="AUTO")
*/
private $id;
/** #var ArrayCollection
* #ORM\ManyToMany(targetEntity="Letter")
* #ORM\JoinTable(name="MailBoxToLetterMapping",
* joinColumns={#ORM\JoinColumn(name="MailBoxId",referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="LetterId",referencedColumnName="id",unique=true)})
*/
private $mailsInBox;
__construct()
{
$mailsInBox = new ArrayCollection();
}
}
class Letter
{
/**
* Holds the Doctrine entity manager for database interaction
* #var EntityManager $em
*/
protected $em;
/**
* #var int
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(stragegy="AUTO")
*/
private $id;
//lets assume this is a string
private $letterContent;
}
Now what im trying to model here is the following:
The Mailbox is somethinglike a container for a bunch of letters.
And a single mailbox can be at a certain point in its life only ba at one place at a time, and only at one. But the containers can be moved in between these two places. i.e. the Postoffice and the PostStorage.
The creation of these Mappings in the SQL database are not the Problem.
Letter N ------ 1 Mailbox (unidirectional with Join Tables)
MailBox N ------1 PostOffice or PostStorage (unidirectional with Join Tables)
The problem comes with the following feature. I want to be able to delete/merge/split individual containers and letters to and from containers and letters.
If im simply trying to delete a letter or Mailbox by using:
$em->remove($InstanceOfLetterOrMailBox);
$em->flush();
im getting the, in the title mentioned, "Integrity constraint violation".
As the Mailboxes can be at different places, i would really like to avoid to add refereces to the owner in the class, as it would bloat up the size of the class code very much, because i would like to add more places for the mailboxes to reside at in the future.
Ive spend the last few hours trying out different combinations of
Ondelete = CASCADE on different classes, but all I managed to do was either delete all classes from the db or not making a single delete, if im not getting the mentioned Error message back.
If you need more information let me know.
Thanks in advance.
I've found a solution to the problem. The problem was not that I was using the Cascade options incorrectly, but that i was trying to delete from an empty mailbox. In other words, I've been using a wrong index for my delete operation. The correct settings i am using now :
"cascade={"remove","persist"},orphanRemoval=true"
as a part of the many to many tag above the collection
I have a Developers entity and table named CodeUserReference
class CodeUserReference
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*#ORM\ManyToOne(targetEntity="Artel\CustomerBundle\Entity\Developers", inversedBy="newreference")
*/
protected $alluser;
/**
* #ORM\Column(type="string", length=255)
*/
protected $codereference;
and Developers
class Developers extends SUser
{
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255, unique=false, nullable=false)
* #Assert\Length(min=3, max=255)
* #Assert\NotBlank
*/
protected $email;
/**
* #ORM\ManyToMany(targetEntity="Artel\CustomerBundle\Entity\CodeUserReference", inversedBy="alluser")
*/
protected $newreference;
I have get query in my Action
public function profileGetAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$code_user_reference = $em->getRepository('ArtelCustomerBundle:CodeUserReference')->findOneByCodereference($request->query->get('reference'));
$user_by_email = $em->getRepository('ArtelCustomerBundle:Developers')->findOneByEmail($request->query->get('email'));
if (!empty($code_user_reference) && empty($user_by_email))
{
$id = $code_user_reference->getAlluser()->getId();
$user_by_reference = $em->getRepository('ArtelCustomerBundle:Developers')->findOneById($id);
$user_by_reference_json = $em->getRepository('ArtelCustomerBundle:Developers')->createQueryBuilder('d')
->where('d.id= :id')
->groupBy('d.id')
->setParameter('id', $code_user_reference->getAlluser()->getId())
->getQuery()->getArrayResult();
echo json_encode( array('user' => $user_by_reference_json));
die;
}
If(empty($code_user_reference) && !empty($user_by_email))
.......
I get Id in code_user_reference, then I find Developers objects with this ID, then I create a QueryBuilder and in this QueryBuilder I find the developers objects again, for JSON table. I find this very hard, who knows easier practics ?
There is certanly mismapping.
if you have #ORM\ManyToOne from one site, there should be #ORM\OneToMany from other.
Or, if you need many-to-many relationship, there should be #ORM\ManyToMany on both entities.
Also, relation should be mapped at one side. If you have (targetEntity="Artel\CustomerBundle\Entity\Developers", inversedBy="newreference") it means it should be mapped here
something like
#ORM\ManyToOne(targetEntity="Artel\CustomerBundle\Entity\Developers", inversedBy="newreference")
#ORM\JoinColumn(name="id_developer", referencedColumnName="id")
and at the other side must be mappedBy instead of inversedBy
* #ORM\OneToMany(targetEntity="Artel\CustomerBundle\Entity\CodeUserReference", mappedBy="alluser")
I configure this entities:
MarketMain:
class MarketMain
{
/**
* #var integer
*
* #ORM\Column(name="id", type="bigint", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="\Acme\CMSBundle\Entity\MarketLanguage", mappedBy="marketMain", indexBy="langId", cascade="all", orphanRemoval=true, fetch="EXTRA_LAZY")
*/
private $marketLanguage;
}
MarketLanguage:
class MarketLanguage
{
/**
* #var \Acme\CMSBundle\Entity\MarketMain
* #ORM\Id
* #ORM\ManyToOne(targetEntity="\Acme\CMSBundle\Entity\MarketMain", inversedBy="marketLanguage")
* #ORM\JoinColumn(name="market_id", referencedColumnName="id")
*/
private $marketMain;
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(name="lang_id", type="integer", nullable=false)
*/
private $langId = 1;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=200, nullable=true)
*/
private $name;
}
And I want to save entity like this:
...........
$form = $this->createForm(new MarketMainType(), new MarketMain());
$form->handleRequest($request);
$marketFormData = $form->getData();
$em->persist($marketFormData);
$em->flush($marketFormData);
foreach ($marketFormData->getMarketLanguage() as $market_language)
{
$market_language->setName("My market name");
$market_language->setMarketMain($marketFormData);
$em->persist($market_language);
}
$em->flush();
Than I get this error:
Entity of type Acme\CMSBundle\Entity\MarketLanguage is missing an
assigned ID for field 'marketMain'. The identifier generation strategy
for this entity requires the ID field to be populated before
EntityManager#persist() is called. If you want automatically generated
identifiers instead you need to adjust the metadata mapping
accordingly.
If I trying to do $marketFormData persist after foreach statment I get this error:
Entity of type Acme\CMSBundle\Entity\MarketLanguage has identity
through a foreign entity Acme\CMSBundle\Entity\MarketMain, however
this entity has no identity itself. You have to call
EntityManager#persist() on the related entity and make sure that an
identifier was generated before trying to persist
'Acme\CMSBundle\Entity\MarketLanguage'. In case of Post Insert ID
Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this
means you have to call EntityManager#flush() between both persist
operations.
I know that If I try to persist $marketFormData before loop doctrine does not know the releated $marketLanguage references, but if I set persist after the foreach it says taht I have first persist parent entity. So I tried this code and it worked:
...........
$form = $this->createForm(new MarketMainType(), new MarketMain());
$form->handleRequest($request);
$marketFormData = $form->getData();
$market_languages = $marketFormData->getMarketLanguage();
$marketFormData->setMarketLanguage(null);
$em->persist($marketFormData);
$em->flush($marketFormData);
$marketFormData->setMarketLanguage($market_languages);
foreach ($marketFormData->getMarketLanguage() as $market_language)
{
$market_language->setName("My market name");
$market_language->setMarketMain($marketFormData);
$em->persist($market_language);
}
$em->flush();
But it is only way to persist related entities? To clone it set to null, persist parent entity, and then set it back, add references and flush all. I think I have missed something here.
I think that your entities is mapped wrong. The entity must have an annotation about ID and another to relation.
And also, when you don't have a primary key with autoincrement, it is necessary declare the class constructor, passing both values as mentioned in http://doctrine-orm.readthedocs.org/en/latest/tutorials/composite-primary-keys.html
It should look like this:
class MarketLanguage
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(name="market_id", type="integer", nullable=false)
*/
private $marketId;
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(name="lang_id", type="integer", nullable=false)
*/
private $langId = 1;
/**
* #var \Acme\CMSBundle\Entity\MarketMain
*
* #ORM\ManyToOne(targetEntity="\Acme\CMSBundle\Entity\MarketMain", inversedBy="marketLanguage")
* #ORM\JoinColumn(name="market_id", referencedColumnName="id")
*/
private $marketMain;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=200, nullable=true)
*/
private $name;
public function __construct($marketId, $langId) {
$this->marketId = $marketId;
$this->langId = $langId;
}
}
Did you try remove flush($marketFormData) ?:
$form = $this->createForm(new MarketMainType(),$marketMain);
$form->handleRequest($request);
$marketFormData = $form->getData();
$em->persist($marketFormData);
// $em->flush($marketFormData); // remove that flush
foreach ($marketFormData->getMarketLanguage() as $market_language)
{
$market_language->setName("My market name");
$market_language->setMarketMain($marketMain);
$em->persist($market_language);
}
$em->flush();
maybe problem is that you are trying flush $marketFormData which contains MarketLanguages which are not persised ? Not sure am i right, didn't tested this.
EDIT maybe this work:
$form = $this->createForm(new MarketMainType(), new MarketMain());
$form->handleRequest($request);
$marketFormData = $form->getData();
foreach ($marketFormData->getMarketLanguage() as $market_language)
{
$market_language->setName("My market name");
$market_language->setMarketMain($marketMain);
}
$em->persist($marketFormData);
$em->flush($marketFormData);
My goal is to return a custom collection for a findAll() query and to deliver this to HAL in order to ensure that its _links are formatted correctly. I originally thought I would simply do this programmatically however this seems to be the wrong way of doing this.
The problem I face is that the data I require is not from a single table, but rather from multiple tables (joins) and I am unable to work out how to do this properly.
I have the following entities:
Stone entity: A standard table with a join to some attributes that I would like to return in my feed
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="Stone\Entity\StAttribute")
* #ORM\JoinTable(name="st_stone_attribute",
* joinColumns={#ORM\JoinColumn(name="stone_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="attribute_id", referencedColumnName="id")}
* )
*
* #var Collection
* #access private
*/
private $attribute;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=50, nullable=false)
*/
private $name;
etc...
The attribute entity is a standard table:
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=30, nullable=false)
*/
private $name;
My resource calls:
public function fetchAll($params = array())
{
return $this->stoneMapper->fetchAll();
}
My mapper file:
public function fetchAll()
{
$qb = $this->stoneRepository->createQueryBuilder('u')
->where('u.state=:state')
->setParameter('state' , 1 );
$adapter = new DoctrineAdapter( new ORMPaginator( $qb ) );
$collection = new StoneCollection($adapter);
return $collection;
}
My collection
use Zend\Paginator\Paginator;
class StoneCollection extends Paginator
{
}
Screen shot of the results here: http://screencast.com/t/vgm34s92dsk2
As you can see from the screen shot "attribute" and other similar fields are not being populated...
So my question is this: how do I ensure that the join tables are populated in the feed?
You will need to fetch join your associations. You can read on this in the Doctrine 2 documentation here.
In your case it would look as follows:
$qb = $this->stoneRepository->createQueryBuilder('s')
->addSelect('a')
->leftJoin('s.attribute', 'a')
->where('s.state = :state')
->setParameter('state' , 1 );
It will also be necessary to have either a hydrator for your StAttribute in your MetadataMap or there should otherwise be some code implemented to extract the StAttribute properties.
You can of course also do this in the fetch method itself, but that is not so pretty.
The object will continue to render as {} in case you do not extract or convert the object to something that can be serialized to valid json format (either a Hal resource or collection instance, a (json) string or a JsonSerializable).
There is probably something I did understand with Doctrine association.
I have a first class :
class FitComments
{
/**
* #var integer
*
* #ORM\Column(name="ID", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="SEQ_FIT_COMMENTS", allocationSize=1, initialValue=1)
*/
private $id;
/**
*
* #ORM\OneToMany(targetEntity="FitCommentsId",mappedBy="comments",cascade={"persist"})
*
*/
private $elements;
/****/
public function __construct()
{
$this->elements=new ArrayCollection();
}
public function getElements()
{
return $this->elements;
}
....
}
And a another Class, the list of elements ID that the comments is link.
/**
* FitCommentsId
* #ORM\Entity
*/
class FitCommentsId
{
/**
* #var integer
*
* #ORM\Column(name="ID", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="SEQ_FIT_COMMENTS_ID", allocationSize=1, initialValue=1)
*/
private $id;
/**
*
* #ORM\ManyToOne(targetEntity="FitComments",inversedBy="elements")
* #ORM\JoinColumn(name="COMMENTS_ID",referencedColumnName="ID")
*/
private $comments;
....
}
I use :
$comments=new FitComment();
$commentId=new FitCommentId();
....
$comments->getElements()->add($commentId);
....
$entityManager->persist($comment);
$entityManager->flush();
But I have a error. $commentId->comments is null. It must be filled with $comment->id normally.
If I must filled manually $commentId->comments, association is not very usefull.
Perhaps I don't understand mechanism.
Note : the SGDB is Oracle.
Try persisting the $commentId also like this:
$comment = new FitComment();
$commentId = new FitCommentId();
....
$comment->getElements()->add($commentId);
....
$entityManager->persist($commentId);
$entityManager->persist($comment);
$entityManager->flush();
No I can't do a 'persist' to $commentId first, because 'persist' to $comment class initiate Oracle sequence for the Id of $comments. (I'm not sure it's very clean what I say....)
'commentID->comments' is link to 'comment->id', and is not null.
I must create $comment at first, and after, create the $commentId.
I know that Doctrine use Sequence before saving record, in persist command. Perhaps I can do a persist without flush at first, and at the end of $commentId recording, do a flush.