Query reference documents ODM Doctrine2 - php

I have two documents Car and Driver
/**
* #ODM\Document(collection="cars")
*/
class Car {
/**
* #ODM\Id
*/
protected $id;
/**
* #ODM\ReferenceOne(targetDocument="Driver")
*/
protected $driver;
//...
}
/**
* #ODM\Document(collection="drivers")
*/
class Driver {
/**
* #ODM\Id
*/
protected $id;
/**
* #ODM\String
* #Assert\NotBlank()
*/
protected $name;
//...
}
I want one car driven by "Peter"
$car = $dm
->getRepository('Car')
->createQueryBuilder()
->field('driver.name')->equals("Peter")
->getQuery()->getSingleResult();
but the previous code return NULL even if the Car and the Driver exist in the database
I found a similar question i want to know if this drawback can be solved by other way

Try this
$car = $dm
->getRepository('Car')
->createQueryBuilder()
->where('driver.name =?1')
->setParameter(1, 'Peter')
->getQuery()->getSingleResult();
EDIT :
If the driver Peter has more then one Car, you should use
->getOneOrNullResult() instead of getSingleResult()

Related

Inheritance relationship with interfaces in symfony/doctrine

I have entities like this :
Request.php (parent)
/**
* #ORM\Entity(repositoryClass=RequestRepository::class)
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({
* "requestA" = "RequestA",
* "requestB" = "RequestB"
* })
*/
abstract class Request
{
/*...*/
}
RequestA.php (child A)
/**
* #ORM\Entity(repositoryClass=DemenagementRepository::class)
*/
class RequestA extends Request implements AddressEntityInterface
{
/**
* #ORM\OneToOne(targetEntity=Address::class, inversedBy="request")
* #ORM\JoinColumn(nullable=false)
*/
private $address;
// +others...
}
RequestB.php (child B)
/**
* #ORM\Entity(repositoryClass=DemenagementRepository::class)
*/
class RequestB extends Request implements AddressEntityInterface
{
/**
* #ORM\OneToOne(targetEntity=Address::class, inversedBy="request")
* #ORM\JoinColumn(nullable=false)
*/
private $address;
// +others...
}
AddressEntityInterface.php
interface AddressEntityInterface
{
public function getAddress(): ?Address;
public function setAddress(Address $address): self;
}
Address.php
/**
* #ORM\Entity(repositoryClass=AddressRepository::class)
*/
class Address
{
/**
* #ORM\OneToOne(targetEntity=??????, mappedBy="address")
*/
private $request;
public function getRequest() { /* ... */ }
}
I want to use getRequest() revert relationship how can i do for make targetEntity dynamically ?
Thanks
I believe you need polymorphic relationships or as they like to call them from docrine 'Inheritance Mapping'
I leave you some sites to view on which you can find everything you need
Official Documentation (Doctrine)
Stackoverflow Practical example
Youtube tutorial
Stackoverflow, discussion <-
personally I advise you to read all this so you get a concrete idea of ​​what you need
I hope my little routing will help you.

Building relationship entity with Neo4J PHP OGM EntityManager

I am trying to build an entity object for my relationship in Neo4j database with GraphAware Neo4j PHP OGM library using this simple method:
public function getRelationshipEntity($entityId) {
$repo = $this->entityManager->getRepository( Entity\Relationship\Fired::class );
return $repo->findOneById($entityId);
}
Here we have the entity classes, relationship first:
namespace Entity\Relationship;
use GraphAware\Neo4j\OGM\Annotations as OGM;
use Entity\Issue;
use Entity\Event;
/**
* #OGM\RelationshipEntity(type="FIRED")
*/
class Fired {
/**
* #OGM\GraphId()
*/
protected $id;
/**
* #OGM\StartNode(targetEntity="Entity\Event")
*/
protected $event;
/**
* #OGM\EndNode(targetEntity="Entity\Issue")
*/
protected $issue;
/**
* #var string
*
* #OGM\Property(type="string")
*/
protected $time;
/**
* #var string
*
* #OGM\Property(type="string")
*/
protected $eventName;
}
Then, start node:
namespace Entity;
use GraphAware\Neo4j\OGM\Annotations as OGM;
/**
* #OGM\Node(label="Event")
*/
class Event {
/**
* #OGM\GraphId()
*/
protected $id;
/**
* #var string
*
* #OGM\Property(type="string")
*/
protected $name;
}
..and end node:
namespace Entity;
use Doctrine\Common\Collections\ArrayCollection;
use GraphAware\Neo4j\OGM\Annotations as OGM;
/**
* #OGM\Node(label="Issue")
*/
class Issue {
/**
* #OGM\GraphId()
*/
protected $id;
/**
* #OGM\Property(type="string")
*/
protected $key;
/**
* #OGM\Property(type="string")
*/
protected $created;
/**
* #OGM\Property(type="string")
*/
protected $updated;
/**
* #OGM\Relationship(type="FIRED", direction="INCOMING", relationshipEntity="Entity\Relationship\Fired", collection=true)
* #var ArrayCollection
*/
protected $eventFires;
public function __construct($key) {
$this->key = $key;
$this->eventFires = new ArrayCollection();
}
public function __wakeup() {
$this->__construct($this->key);
}
/**
* #return ArrayCollection
*/
public function getEventFires() {
return $this->eventFires;
}
public function addEventFire(Entity\Relationship\Fired $eventFired) {
$this->eventFires->add($eventFired);
}
public function removeEventFire(Entity\Relationship\Fired $eventFired) {
$this->eventFires->removeElement($eventFired);
}
}
Apparently, what works really well for node entites, triggers the following error for relationships:
Fatal error: Call to undefined method GraphAware\Neo4j\OGM\Metadata\RelationshipEntityMetadata::hasCustomRepository() in /vendor/graphaware/neo4j-php-ogm/src/EntityManager.php
Any suggestion how I could workaround this? I even tried using EntityManager::createQuery() the following way:
public function getRelationships($eventName) {
$query = $this->entityManager->createQuery('MATCH (e:Event)-[f:FIRED{eventName: {eventName}}]->() RETURN e,f ORDER BY f.time');
$query->addEntityMapping('e', 'Entity\Event' );
$query->addEntityMapping('f', 'Entity\Relationship\Fired' );
$query->setParameter( 'eventName', $eventName);
return $query->execute();
}
But, apparently, addEntityMapping() doesn't work for relationship entities either! (It might be a feature though, not a bug):
Catchable fatal error: Argument 1 passed to GraphAware\Neo4j\OGM\Hydrator\EntityHydrator::hydrateNode() must implement interface GraphAware\Common\Type\Node, instance of GraphAware\Bolt\Result\Type\Relationship given, called in /vendor/graphaware/neo4j-php-ogm/src/Query.php on line 145 and defined in /vendor/graphaware/neo4j-php-ogm/src/Hydrator/EntityHydrator.php on line 232
So, I ended up that I can easily define and store relationship entities in Neo4J with this library but not sure how I could retrieve it easily with EntityManager, in the similar way I can do so with nodes.
Any help would be much appreciated!
As requested in comment below, these are GraphAware packages that I am using:
graphaware/neo4j-bolt 1.9.1 Neo4j Bolt Binary Protocol PHP Driver
graphaware/neo4j-common 3.4.0 Common Utilities library for Neo4j
graphaware/neo4j-php-client 4.8.0 Neo4j-PHP-Client is the most advanced PHP Client for Neo4j
graphaware/neo4j-php-ogm 1.0.0-RC6 PHP Object Graph Mapper for Neo4j

Add brands through company, it's possible? How?

I have this two tables (see pics below) mapped as follow:
class Brand
{
...
/**
* #var Company
*
* #ORM\ManyToOne(targetEntity="Company")
* #ORM\JoinColumn(name="companies_id", referencedColumnName="id")
*/
protected $company;
}
class Company
{
...
}
I need to add support for add a new Brand from Company but I have not idea in how to achieve this. This are handled through SonataAdminBundle but I think I need to add something else to entities in order to create brands from company but I am not sure what this would be, can I get some help? I am stucked
1st attempt
After get an answer this is how I modify Company entity:
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
class Company
{
...
/**
* #var Brand
* #ORM\OneToMany(targetEntity="Brand", mappedBy="company", cascade={"persist"})
**/
protected $brands;
public function __construct()
{
$this->brands = new ArrayCollection();
}
...
public function getBrands()
{
return $this->brands;
}
/**
* Add brands
*
* #param Brand $brand
* #return Brands
*/
public function addBrand( Brand $brand)
{
$this->brands[] = $brand;
return $this;
}
/**
* Remove brands
*
* #param Brand $brand
*/
public function removeBrand( Brand $brand)
{
$this->brands->removeElement($brand);
}
}
But I am getting this error:
No entity manager defined for class
Doctrine\Common\Collections\ArrayCollection
Why is that?
You could try setting up your entities like this:
class Brand
{
/**
* #var Company
*
* #ORM\ManyToOne(targetEntity="Company", inversedBy="brands")
* #ORM\JoinColumn(name="companies_id", referencedColumnName="id")
*/
protected $company;
}
class Company
{
/**
* #var ArrayCollection
*
* #OneToMany(targetEntity="Brand", mappedBy="company", cascade={"persist"})
**/
protected $brands;
}
What we're defining here is that new Brands can be created from the Company entity with cascade={"persist"}.
It's recommended you implement addBrand and removeBrand in Company for direct interaction with the ArrayCollection.
A simple example of the final functionality:
$company = $service->getCompany(1); // our company entity
$brand = new Brand();
$brand->set...
...
$company->addBrand($brand);
$entityManager->persist($company);
EDIT
This is just an example, you may choose not to add with keys or even implement a remove function, but this is a starting point:
public function addBrand(Brand $brand)
{
// key needs to be something that can uniquely identify the brand
// e.g. name
$this->getBrands()->set(*key*, $brand);
return $this;
}
public function removeBrand($key)
{
$this->getBrands()->remove($key);
return $this;
}

Symfony2 DoctrineMongoDBBundle one-to-many Bi-Directional References

I'm trying to use relations in MongoDB, using Symfony2 and DoctrineMongoDBBundle
According slide 49 of the Doctrine MongoDB Object Document Mapper presentation,
it's enough to assign $User->setOrganization($Organization), to make $Organization::users[0] referred to user object.
In the documentation says i have to use inversedBy and mappedBy options.
I have the similar scheme (User belongs to Group), but I can't get both update work:
$Group = new \MyVendor\MongoBundle\Document\Group();
$User = new \MyVendor\MongoBundle\Document\User();
$User->setGroup($Group);
/** #var \Doctrine\ODM\MongoDB\DocumentManager $dm */
$dm = $this->get('doctrine_mongodb')->getManager();
$dm->persist($Group);
$dm->persist($User);
$dm->flush();
Results in MongoDB:
Group
{
"_id": ObjectId("5043e24acdc2929a0500000d"),
}
User
{
"_id": ObjectId("5043e24acdc2929a0500000c"),
"group": {
"$ref": "Group",
"$id": ObjectId("5043e24acdc2929a0500000d"),
"$db": "my_db"
}
}
src/MyVendor/MongoBundle/Document/User.php
<?php
namespace MyVendor\MongoBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document(repositoryClass="MyVendor\MongoBundle\Repository\UserRepository")
*/
class User
{
/**
* #MongoDB\Id
*/
private $id;
/**
* #var
* #MongoDB\ReferenceOne(targetDocument="Group", inversedBy="users")
*/
private $group;
/**
* Set group
*
* #param MyVendor\MongoBundle\Document\Group $group
* #return User
*/
public function setGroup(\MyVendor\MongoBundle\Document\Group $group)
{
$this->group = $group;
return $this;
}
}
src/MyVendor/MongoBundle/Document/Group.php
<?php
namespace MyVendor\MongoBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class Group
{
/**
* #MongoDB\Id
*/
private $id;
/**
* #MongoDB\ReferenceMany(targetDocument="User", mappedBy="group")
* #var User[]
*/
private $users;
public function __construct()
{
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add users
*
* #param MyVendor\MongoBundle\Document\User $users
*/
public function addUsers(\MyVendor\MongoBundle\Document\User $users)
{
$this->users[] = $users;
}
}
The question is why do you need $refs in both documents? That's not an effective way because you need to maintain two objects separately. If you really need it, then you need to set references on both ends.
public function setGroup(\MyVendor\MongoBundle\Document\Group $group)
{
$this->group = $group;
$group->addUsers($this);
return $this;
}
The second option is to keep $ref only on one of the documents. Doctrine will handle all the job for you. For this to work you only need to set inverse and owning side (don't need to use $group->addUsers($this);).
For user:
* #MongoDB\ReferenceOne(targetDocument="Group", inversedBy="users")
For Group:
* #MongoDB\ReferenceMany(targetDocument="User", mappedBy="group")
And it's always better to use the documentation than presentations.
ps: the OP changed the question according to this answer. Check the history before downvoting correct answers.

Slow Doctrine Find

I am trying to figure out why one of my doctrine finds is running so slow. I don't really know where to start, so please bear with me.
I do a pretty basic find to fetch a user object. This find is taking ~160ms. When I run the query via phpmyadmin, it takes .7ms.
$this->em->find('Entities\User', $userId)
I have already tried adding skip-name-resolve to mysql's my.cnf. The id field in the user table is indexed. I really don't know what else to try. Let me know if there is additional information I can provide.
Below is the entity file:
namespace Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityRepository;
/** #Entity(repositoryClass = "Entities\UserRepository")
* #Table(name="user")
*/
class User extends \Company_Resource_AbstractEntity
{
/** #Id #Column(type="integer") #GeneratedValue */
protected $id;
/** #Column(type="string") */
protected $name;
/** #Column(type="string") */
protected $password;
/** #Column(type="string") */
protected $email;
/** #Column(type="string") */
protected $first_name;
/** #Column(type="string") */
protected $last_name;
/** #Column(type="integer") */
protected $password_reset;
/** #Column(type="string") */
protected $salt;
/** #Column(type="integer") */
protected $active;
/** #Column(type="string") */
protected $cookie_hash;
/**
* #ManyToOne(targetEntity="Company" , inversedBy="user")
*/
protected $company;
/**
* #ManyToOne(targetEntity="Privilege" , inversedBy="user")
*/
protected $privilege;
/**
* #OneToMany(targetEntity="CompanySubscription" , mappedBy="user")
*/
protected $subscription;
/**
* #OneToMany(targetEntity="EquipmentEvent" , mappedBy="check_in_user")
*/
protected $check_in;
/**
* #OneToMany(targetEntity="EquipmentEvent" , mappedBy="check_out_user")
*/
protected $check_out;
/**
* #OneToMany(targetEntity="GroupEvent" , mappedBy="check_in_user")
*/
protected $check_in_group;
/**
* #OneToMany(targetEntity="GroupEvent" , mappedBy="check_out_user")
*/
protected $check_out_group;
/**
* #OneToMany(targetEntity="Maintenance" , mappedBy="submit_user")
*/
protected $maintenance_submit;
/**
* #OneToMany(targetEntity="Maintenance" , mappedBy="completed_user")
*/
protected $maintenance_complete;
/**
* #OneToMany(targetEntity="UserLogin" , mappedBy="user")
*/
protected $login;
}
Abstract entity:
use \Doctrine\Common\Collections\ArrayCollection;
abstract class Company_Resource_AbstractEntity implements ArrayAccess
{
public function offsetExists($offset)
{
return property_exists($this, $offset);
}
// The get/set functions should check to see if an appropriately named function exists before just returning the
// property. This way classes can control how data is returned from the object more completely.
public function offsetGet($offset)
{
$property = new Zend_Filter_Word_UnderscoreToCamelCase();
$method = 'get'. $property->filter($offset);
return $this->{$method}();
}
public function offsetSet($offset, $value)
{
$property = new Zend_Filter_Word_UnderscoreToCamelCase();
$method = 'set'. $property->filter($offset);
return $this->{$method}($value);
}
public function offsetUnset($offset)
{
// can't do this
}
/*==-====-====-====-====-====-====-====-====-====-====-==*/
/*
* Provides magic method access for getFieldName() and setFieldName()
* where field_name is a simple field and not a relation
* A special getData implementation returns all of the current object vars
*/
public function __call($method, $arguments)
{
preg_match('#^([a-z]+)(.*)#', $method, $matches);
$action = $matches[1];
$property = $matches[2];
$underscore = new Zend_Filter_Word_CamelCaseToUnderscore();
$offset = strtolower($underscore->filter($property));
if ($action == 'get')
{
if ($property == 'Data')
return get_object_vars($this);
if ($this->offsetExists($offset))
return $this->{$offset};
else
throw new Zend_Exception(sprintf("'%s' does not have property '%s'", get_class($this), $offset));
}
else if ($action == 'set')
{
if ($this->offsetExists($offset))
return $this->{$offset} = $arguments[0];
else
throw new Zend_Exception(sprintf("'%s' does not have property '%s'", get_class($this), $offset));
}
else
throw new Zend_Exception(sprintf("'%s' does not have method '%s'", get_class($this), $method));
}
}
The SQL that the find produces:
SELECT t0.id AS id1,
t0.name AS name2,
t0.password AS password3,
t0.email AS email4,
t0.first_name AS first_name5,
t0.last_name AS last_name6,
t0.password_reset AS password_reset7,
t0.salt AS salt8,
t0.active AS active9,
t0.cookie_hash AS cookie_hash10,
t0.company_id AS company_id11,
t0.privilege_id AS privilege_id12
FROM user t0 WHERE t0.id = ?
Anyone see anything wrong or know where to go further with this?
Using Doctrine 2.2.2.
The explain I get when I run that query with phpmyadmin: http://i.imgur.com/wWeGO.png
The table schema: http://i.imgur.com/BQsRX.jpg
I believe the problem with my setup was the actual number of lines in the file. Doctrine was reading through those every time. I enabled APC for the meta-cache and load time decreased dramatically after the first load. Without query or result cache, that query ACTUALLY only takes about 6 MS which is what I was aiming for all along. Wish I would have tried that sooner.

Categories