Check If Record Has References - doctrine-orm

I wonder if there is any way to find out if a record has references.
I have the following entity:
class Item extends Entity
{
/**
* #Id #GeneratedValue
* #Column(name="id")
*/
protected $id;
/**
* #OneToMany(targetEntity="Citation", mappedBy="citation")
*/
protected $citedIn;
/**
* #Column(name="stamped", type="boolean")
*/
protected $stamped;
/** ... rest of the code ... */
}
And I want to perform the following check:
$qb
->select("
COUNT(i.id),
WHEN (i.stamped = true) THEN 'stamped'
WHEN (i.citedIn IS NOT NULL) THEN 'cited'
ELSE 'just started'
END current_status
")
->from(Entity\Item::class, 'i')
->groupBy('current_status');
Note that I tried to use i.citedIn IS NOT NULL, but it didn't work. What could I use instead?

Since it is a OneToMany relationship I think you need to use IS NOT EMPTY instead of IS NOT NULL. Doctrine will create an empty ArrayCollection for each entity, so technically it's not null, because it's an empty array.
Update
If that doesn't work, try NOT INSTANCE OF YourApp\Model\Citation.

Related

How to join column and target entity manually in manytoone relation?

I have two fields : account_type_id and account_id.
How can i manually map doctrine TargetEntity to join Company Entity if accountTypeId = 1 OR join User Entity if account_type_id = 2 ?
<?php
/** #Entity */
class Accounts
{
// 1= Company, 2 = User
private $accountType;
/**
* #ManyToOne(targetEntity="Companies")
*/
private $company;
/**
* #ManyToOne(targetEntity="Users")
*/
private $user;
//...
}
Unfortunately, joining different columns on the fly cannot be done automatically, but you can have both fields set as nullable and only set the correct one when persisting the Account entity.
This would be the annotation:
/**
* #ORM\ManyToOne(targetEntity="Users", inversedBy="users")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true)
*/
private $user;
Keep in mind that nullable=true is the default anyway, I'm just being specific here.
If you want to go defensively about this, you can have an additional check in getter
/**
* #return User
* #throws \Exception
*/
public function getUser()
{
if ($this->accountType !== 2) {
throw new \Exception("Entity is not of type 'user'");
}
return $this->user;
}

Index not created

I have a document with an index defined.
/** #ODM\Document */
class Stat
{
/** #ODM\Id(strategy="NONE", type="int") */
private $id;
/** #ODM\Field(type="string") */
private $period;
/** #ODM\ReferenceOne(targetDocument="Player") */
private $player;
/** #ODM\Field(type="string") #ODM\Index */
private $statType;
However, when I look at the indexes created in the collection, the only index is the _id.
To save the document I'm doing:
$stat = new Documents\Stat();
$stat->setId(1);
$stat->setPlayer($player);
$stat->setPeriod(1);
$stat->setStatType('goal');
$dm->persist($stat);
$dm->flush();
Is there something else I'm not doing?
Indexes are not created (nor ensured if they're in sync with mapping) during normal ODM usage.
If you're using Symfony then solving problem is easy, just run doctrine:mongodb:schema:create command from Symfony's console (and if you're using other framework, look for equivalent as command itself is provided by ODM).
If you want to control indexes from your code you can use SchemaManager which provides an API to control "schema" directly (you can also control indexes on per-document basis). Obtaining it is easy: $sm = $dm->getSchemaManager();.
// AppBundle/Document
namespace AppBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Doctrine\Bundle\MongoDBBundle\Validator\Constraints\Unique as MongoDBUnique;
/**
* #MongoDB\Document(repositoryClass="AppBundle\Repository\ProduitRepository")
* #MongoDBUnique(fields="$noProduit")
*/
class Produit
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
* #MongoDB\UniqueIndex(order="asc")
*/
protected $noProduit;
/**
* #MongoDB\Field(type="string")
*/
protected $libelle;
// getter and setter
}
// AppBundle/Controller
$dm = $this->get('doctrine_mongodb')->getManager();
$dm->getSchemaManager()->ensureIndexes();
schema and database will be created automatically when you run your app.
For more info
visit this link
/**
* #ODM\Document
* #ODM\Indexes({
* #ODM\Index(keys={"statType"="asc"}),
* #ODM\UniqueIndex(keys={"period"="asc"})//For example unique index
* })
*/
class Stat
{
}
Then if you use symfony run php bin/console doctrine:mongodb:schema:create

Doctrine2, uniqueConstraint on JoinColumn for ZF2?

I have a data feed that has duplicated content (no idea why, it's an external feed), however we need to insert all items with a constraint on the title and type, i.e.
These can exist:
Name_A, Type_A
Name_A, Type_B
But only one of these should exist:
Name_A, Type_A
Name_A, Type_A
Here's the Entity code I'm using:
/**
* Restauration
*
* #ORM\Table(name="restauration", uniqueConstraints={#ORM\UniqueConstraint(name="name_unique", columns={"name_1", "restauration_type"})})
* #ORM\Entity(repositoryClass="iMotionTools\Repository\RestaurationRepository")
*/
class Restauration
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name_1", type="string", length=128, nullable=true)
*/
private $name1;
/**
* #var string
*
* #ORM\ManyToOne(targetEntity="RestaurationType", cascade={"persist"})
* #ORM\JoinColumn(name="restauration_type", referencedColumnName="id")
*/
private $type;
}
But I get this error when parsing and inserting the data:
SQLSTATE[23000]: Integrity constraint violation: 19 columns name_1, restauration_type are not unique:91:C:\coding\currate\vendor\doctrine\dbal\lib\Doctrine\DBAL\DBALException.php
I'm wondering whether the easy way is to just ignore the thrown exception? Looks like it's a driverExceptionDuringQuery that gets thrown during my call to $em->persist(); but I'm not sure how I would ignore if the call contains the above error?
If you want to ignore that, stop using constraint integrity.
{#ORM\UniqueConstraint(name="name_unique", columns={"name_1" //etc...
Your data has name_1 not unique this is why you have this error, integrity constraint check this, you can't ignore that, without remove the unique constraint parameters.
Edit :
You have to then, before persist data, check with your actual data in the table, if there is a duplicate entry for both name_1 AND Type, and do not persist them.
for check both you can use :
#UniqueEntity({"name", "type"})
found here :
validation multiple constraint columns
Even if it's for SF2, it's the same concept
I've removed the UniqueConstraint attribute from the table and added a function to check the objects list (which later get $entity->persist()-ed), using an array so that I can easily use it for different entity types, and it seems to work well now.
$key = $hashList ? '' : $page['id'];
foreach ($hashList as $method) {
$val = $object->{$method}();
if(is_object($val)) {
$val = $val->getId();
}
$key .= $val;
}
$key = md5($key);
$objects[$key] = $object;
Where $hashList = array('getName', 'getType') - and getType returns an object (since it's another entity), but which always has the getId() function... probably not the best solution but it works for my situation...

doctrine2 OneToMany relationship inserts NULL as the foreign key

I have a bi-directional OneToMany relationship using Doctrine 2 as an ORM within the ZendFramework 1.11.2.
Note: Doctrine did not create the database tables. The database is MySQL.
For some reason, when I persist and flush a new link entity to the link table (see below) the foreign key field (container_id) gets set to NULL. However, if the '#' symbol is removed from the 'ManyToOne(targetEntity="Shepherd\Navigation\Domain\Container\Model", inversedBy="links")' line, the foreign key field is populated properly.
Since the entity is added properly to the database when the '#' symbol is removed, there is something wrong with the OneToMany relationship somewhere.
For example, if I have a link model named $link (see pseudo-code below)...
$link (Shepherd\Navigation\Domain\Link\Model)
{
id: '' // auto generated value
cid: 23 // the foreign key value
label: test
uri: test.com
... // other values not listed here for brevity
}
...when the new link model is persisted and the entity manager is flushed, the container_id (foreign key) value from the newly inserted row in the link (shepherd_navigation_link) table is NULL.
$em // Assume $em is the Entity Manager
$em->persist($link);
$em->flush();
// The container_id in the newly added row in the
// link table (shepherd_navigation_link) is NULL
The link table schema:
CREATE TABLE `shepherd_navigation_link` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`container_id` int(10) unsigned DEFAULT NULL,
`node_id` int(10) unsigned DEFAULT NULL,
`parent_id` int(10) unsigned DEFAULT NULL,
`label` varchar(100) NOT NULL,
`options` text,
`events` text,
`privilege` varchar(100) NOT NULL,
`resource` varchar(100) DEFAULT NULL,
`uri` varchar(300) NOT NULL,
`visible` int(10) unsigned DEFAULT '1',
PRIMARY KEY (`id`),
KEY `container_id` (`container_id`)
) ENGINE=InnoDB
ALTER TABLE `shepherd_navigation_link` ADD FOREIGN KEY (container_id) REFERENCES shepherd_navigation_container(id)
Link entity model:
/**
* #Entity
* #Table(name="shepherd_navigation_link")
*/
class
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #Column(name="container_id", type="integer", nullable=false)
*/
protected $cid;
/**
* #Column(name="node_id", type="integer")
*/
protected $nid;
/**
* #Column(name="parent_id", type="integer", nullable=false)
*/
protected $pid;
/**
* #Column
*/
protected $label;
/**
* #Column(nullable=true)
*/
protected $options;
/**
* #Column(nullable=true)
*/
protected $events;
/**
* #Column
*/
protected $privilege;
/**
* #Column(nullable=true)
*/
protected $resource;
/**
* #Column
*/
protected $uri;
/**
* #Column(type="integer", nullable=true)
*/
protected $visible;
/**
* #OneToMany(targetEntity="Model", mappedBy="parent")
*/
private $children;
/**
* #ManyToOne(targetEntity="Model", inversedBy="children")
*/
private $parent;
/**
*) #ManyToOne(targetEntity="Shepherd\Navigation\Domain\Container\Model", inversedBy="links"
*/
private $container;
/**
* #OneToOne(targetEntity="Shepherd\Navigation\Domain\Link\Position", inversedBy="link")
*/
private $node;
public function __construct()
{
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
}
/** Accessors and Mutators excluded for brevity **/
}
Note: the protected property $cid maps to the container_id column above.
The container table schema:
CREATE TABLE `shepherd_navigation_container` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`description` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
The container entity model:
/**
* #Entity
* #Table(name="shepherd_navigation_container")
*/
class Model
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #Column
*/
protected $name;
/**
* #Column(nullable=true)
*/
protected $description;
/**
* #OneToMany(targetEntity="Shepherd\Navigation\Domain\Link\Model", mappedBy="container")
*/
private $links;
/**
* Constructor
*/
public function __construct()
{
$this->links = new \Doctrine\Common\Collections\ArrayCollection();
}
/** Accessors and Mutators excluded for brevity **/
}
What am I missing? What am I doing wrong?
I figured out the problem (by reading the documentation http://www.doctrine-project.org/docs/orm/2.0/en/tutorials/getting-started-xml-edition.html). It turns out there were actually a few problems.
Problem 1 => I did not provide a method to set the container variable.
// Inside the Link Entity class...
public function setContainer($container)
{
$this->container = $container;
}
Problem 2 => I did not set the container value. In error, I thought Doctrine 2 did this internally, but I found out the container variable needs to be set prior to flushing.
Foolish oversight on my part.
$link = new Link();
$link->setContainer($container);
// $em is the Entity Manager
$em->persist($link);
$em->flush();
Problem 3 => The container ($container) needed to either be persisted prior to flushing or the #OneToMany definition on the container entity needed to change. I chose to update the container entity definition. Take a look here (http://www.doctrine-project.org/docs/orm/2.0/en/reference/working-with-associations.html#transitive-persistence-cascade-operations) for more information.
// Inside the Container Entity class...
/**
* #OneToMany(targetEntity="Shepherd\Navigation\Domain\Link\Model", mappedBy="container", cascade={"persist"})
*/
After making these changes and removing the #OneToOne node relationship in the link entity class (turns out I didn't need it), everything worked fine. I hope this helps someone.

Doctrine 2 - Insert new item in database

I'm trying to make something very simple.. but I do wrong, and I don't know what is the problem. Just I'm trying to insert new item to database with Doctrine 2:
$favouriteBook = new UserFavouriteBook;
$favouriteBook->user_id = 5;
$favouriteBook->book_id = 8;
$favouriteBook->created_at = new DateTime("now");
$this->_em->persist($favouriteBook);
$this->_em->flush();
As you can see.. is very simple, but that, give me next error:
Error: Message: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'user_id' cannot be null
Obviosly, if I make a "dump" before "persist" and "flush" of $favouriteBook, all looks be correct..
This is my "favouriteBook" entity:
/** #Column(type="integer")
* #Id
*/
private $user_id;
/** #Column(type="integer")
* #Id
*/
private $book_id;
/**
* #ManyToOne(targetEntity="Book", inversedBy="usersFavourite")
* #JoinColumn(name="book_id", referencedColumnName="id")
*/
private $book;
/**
* #ManyToOne(targetEntity="User", inversedBy="favouriteBooks")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/** #Column(type="datetime") */
private $created_at;
public function __get($property) {
return $this->$property;
}
public function __set($property, $value) {
$this->$property = $value;
}
Anyone can image what is the problem? .. I don't know what else try.. Thanks
I think what beberlei is saying is that within your favouriteBook entity, you don't need to define the user_id and book_id as class properties, b/c the book and user properties you have set already recognize these as the relevant join columns. Also, your attempt to persist the favouriteBook entity failed because you need to set the book and user entity associations within the favouriteBook entity, not the foreign keys. So it would be:
$favouriteBook = new UserFavouriteBook;
$favouriteBook->book = $book;
$favouriteBook->user = $user;
$favouriteBook->created_at = new DateTime("now");
$this->_em->persist($favouriteBook);
$this->_em->flush();
You are mapping foreign keys and the associations. You have to modify the association not the foreign key field. Its bad-practice to map them both, you should remove $book_id and $user_id completly.