Symfony2/Doctrine2 model classes mappings for reusable bundle - php

I am currently trying to create a reusable bundle with Symfony2 using model classes but I am not able to register their mappings so Doctrine recognize them.
I read that using compiler pass could be the solution so I followed the guide in the Symfony Cookbook (http://symfony.com/doc/current/cookbook/doctrine/mapping_model_classes.html) and I also looked at the source code in the FOSUserBundle for some inspiration.
And here is what I've done so far :
class GPGoodsBundle extends Bundle{
public function build(ContainerBuilder $container)
{
parent::build($container);
$this->addRegisterMappingsPass($container);
}
/**
* #param ContainerBuilder $container
*/
private function addRegisterMappingsPass(ContainerBuilder $container)
{
$modelDir = realpath(__DIR__.'/Resources/config/doctrine/model');
$mappings = array(
$modelDir => 'GP\Bundle\GoodsBundle\Model',
);
$ormCompilerClass = 'Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass';
if (class_exists($ormCompilerClass)) {
$container->addCompilerPass(
DoctrineOrmMappingsPass::createXmlMappingDriver(
$mappings,
array('gp_goods.model_manager_name'),
'gp_goods.backend_type_orm'
)
);
}
}
}
But when trying to migrate my entity (just to see if it's working) here is the result :
$php app/console doctrine:migrations:diff
No mapping information to process.
My entities are stored under "GP\Bundle\GoodsBundle\Model" and their mappings under "GP\Bundle\GoodsBundle\Resources\config\doctrine\model"
So my question is : what is the good way to create a reusable bundle and how to register mappings of model classes?
If you need any additional information, do not hesitate to ask!
Thank you for your help!
Here is one of my model classes :
class Good implements GoodInterface{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
protected $serial;
/**
* #var \DateTime
*/
protected $manufacturedAt;
/**
* #var \DateTime
*/
protected $deliveredAt;
/**
* #var \DateTime
*/
protected $expiredAt;
/**
* #var string
*/
protected $checkInterval;
/**
* #var string
*/
protected $status;
/**
* #var string
*/
protected $slug;
/**
* #var \DateTime
*/
protected $createdAt;
/**
* #var \DateTime
*/
protected $updatedAt;
/**
* #var \DateTime
*/
protected $deletedAt;
public function __construct(){
$this->createdAt = new \DateTime("now");
$this->status = 'good';
}
.... getters/setters .....
}
And the mappings :
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="GP\Bundle\GoodsBundle\Model\Good" table="good">
<id name="id" type="integer" column="id">
<generator strategy="AUTO"/>
</id>
<field name="serial" type="string" column="serial" length="255"/>
<field name="manufacturedAt" type="date" column="manufactured_at"/>
<field name="deliveredAt" type="date" column="delivered_at"/>
<field name="expiredAt" type="date" column="expired_at"/>
<field name="checkInterval" type="string" column="check_interval" length="255" nullable="true"/>
<field name="status" type="string" column="status" length="255"/>
<field name="slug" type="string" column="slug" length="255">
<gedmo:slug fields="serial" unique="true" />
</field>
<field name="createdAt" type="datetime" column="created_at">
<gedmo:timestampable on="create"/>
</field>
<field name="updatedAt" type="datetime" column="updated_at" nullable="true">
<gedmo:timestampable on="update"/>
</field>
<field name="deletedAt" type="datetime" column="removed_at" nullable="true">
<gedmo:soft-deleteable field-name="deletedAt"/>
</field>
</entity>
</doctrine-mapping>

When you have entities outside of any bundle or that the location is not the usual one, you'll have to change the doctrine section in config.yml from
doctrine:
# ...
orm:
# ...
auto_mapping: true
to
doctrine:
# ...
orm:
# ...
mappings:
model: # replace `model` with whatever you want
type: annotation # could be xml, yml, ...
dir: %kernel.root_dir%/../path/to/your/model/directory
prefix: Prefix\Namespace # replace with your actual namespace
alias: Model # replace with the desired alias
is_bundle: false
The dir parameter informs Doctrine where to look for the mapping definitions. If you're using annotations, it will be your model directory. Otherwise, it will be the directory of your xml/yml files.
Entities's names — to access from Doctrine repositories — begin with Model in this case, for example, Model:User. It corresponds to the parameter alias.
When editing a configuration file, do not forget to clear the cache.
Moreover, in my question, I wrote that I changed something in my Bundle class but it was not useful as the bundle won't be reused by another project. So I removed everything.
See this answer for more details :
https://stackoverflow.com/a/10001019/2720307
Thanks to Elnur Abdurrakhimov!

Related

One Entity used in many, many-to-one relationships

I have following entities:
class File{
private $fileId;
private $filePath;
}
class User{
private $userId;
private array $userFiles;
}
class Order{
private $orderId;
private array $orderFiles;
}
I have three entities: File, User, Order.
Now I want to link User to File as one2many relationships. One user can have multiple files. And the same with Order - connect Order with File via one2many relationship - one order can have multiple files.
My question is, do I need to create two(one for relation with User, and one for relation with Order) tables for the File entity?
Or I can merge these two tables into one and for instance, add field type in File entity, which will tell us to which entity(User/File) it belongs.
Here you have my uncomplete mapping in xml
<entity name="File" table="file">
<id name="fileId" type="uuid">
<generator strategy="NONE" />
</id>
<field name="filePath />
</entity>
<entity name="User" table="user">
<id name="userId" type="uuid">
<generator strategy="NONE" />
</id>
<one-to-many field="userFiles" target-entity="File" mapped-by="herewhat?" >
<cascade>
<cascade-persist />
</cascade>
</one-to-many>
</entity>
<entity name="Order" table="order">
<id name="orderId" type="uuid">
<generator strategy="NONE" />
</id>
<one-to-many field="orderFiles" target-entity="File" mapped-by="herewhat?" >
<cascade>
<cascade-persist />
</cascade>
</one-to-many>
</entity>
I would prefer the second option - everything in one table if it is possible.
Thanks in advance!
I think the best option is to have two separate tables like order_files/user_files instead of stiffing everything in 1 table and relay on something like type(string) to get your collection afterwords. Also now this seems to be very simple table(files I mean), but with the time it can developed more and you may need different fields for the two different file types. So if you take this option you can build the relations b/n the tables as(for example the Order/orderFiles):
...
/**
* #var Collection|OrderFiles[]
* #ORM\OneToMany(targetEntity="PathToMyEntity\OrderFile", mappedBy="order")
*/
protected $orderFiles;
Then function you need add remove items in this collection, something like this:
....
/**
* #param $orderFiles
*
* #return $this
*/
public function addOrderFiles($orderFiles)
{
$this->orderFiles = $orderFiles;
return $this;
}
/**
* #param $orderFiles
*
* #return $this
*/
public function removeOrderFiles($orderFiles)
{
foreach ($orderFiles as $orderFile) {
$this->orderFiles->removeElement($orderFile);
}
return $this;
}
/**
* Add orderFile
*
* #param OrderFile $orderFile
*
* #return Order
*/
public function addOrderFile(OrderFile $orderFile)
{
$this->orderFiles[] = $orderFile;
return $this;
}
/**
* Remove orderFile
*
* #param OrderFile $orderFile
*/
public function removeOrderFile(OrderFile $orderFile)
{
$this->orderFiles->removeElement($orderFile);
}
/**
* Get orderFile
*
* #return Collection|OrderFile[]
*/
public function getOrderFiles()
{
return $this->orderFiles;
}
....
Now lets manage this relation on the file class:
/**
* #ORM\ManyToOne(targetEntity="PathToMyEntity\Order", inversedBy="orderFiles")
* #ORM\JoinColumn(name="order_id", referencedColumnName="id", nullable=false)
*/
protected $order;
And the get set methods:
/**
* Get order
*
* #return Order
*/
public function getOrder()
{
return $this->order;
}
/**
* Set order
*
* #param Order $order
*
* #return self
*/
public function setOrder(Order $order)
{
$this->order = $order;
return $this;
}
This is just a suggestion but it would be nice if some Doctrine guru gives opinion on this matter too :)

Doctrine orm mapping returns error Undefined index

I'm trying to write a code that maps two to entities but in the result,
I get an error Undefined index: filterId
So without doctrine, the query is simple SELECT * FROM filter f INNER JOIN filter_options fo ON f.id = fo.filter_id WHERE f = 1;
So I need to get the same results as this query would give.
tables example:
filter
id|status
---------
1|active
2|active
3|active
filter_options:
id|filter_id|text
---------
1|1|lorem
2|1|ipsum
3|3|and
4|2|etc
Entity classes :
class Filter
{
private $id;
private $status;
/**
* #var FilterOption[]
*/
private $options;
}
class FilterOption
{
private $id;
private $filterId;
private $text;
}
Filter.orm.xml:
<id name="id" column="id" type="integer">
<generator strategy="AUTO" />
</id>
<field name="status" column="status"/>
<one-to-many field="options" target-entity="FilterOption" mapped-by="filterId"/>
FilterOption.orm.xml:
<id name="id" column="id" type="integer">
<generator strategy="AUTO" />
</id>
<field name="text" column="text"/>
<field name="filterId" column="filter_id"/>
What I'm doing wrong. I want to get Filter entity in which property $options in an array with all other filterOptions entities.
You need to think about relation in Doctrine as of relation between objects, not as relation into database. Because of this you should not think about database columns like filter_id as plain properties, they needs to be replaced with actual entity associations instead.
In your case you need to replace plain filterId property into FilterOption entity with inverse side of Many-to-One association. Please refer Doctrine documentation for details.
Your code may look something like this:
Filter.php
class Filter
{
/**
* #var int
*/
private $id;
/**
* #var string
*/
private $status;
/**
* #var FilterOption[]
*/
private $options;
}
FilterOption.php
class FilterOption
{
/**
* #var int
*/
private $id;
/**
* #var Filter
*/
private $filter;
/**
* #var string
*/
private $text;
}
Filter.orm.xml:
<doctrine-mapping>
<entity name="Filter" table="filter">
<id name="id" column="id" type="integer">
<generator strategy="AUTO" />
</id>
<field name="status" column="status"/>
<one-to-many field="options" target-entity="FilterOption" mapped-by="filter"/>
</entity>
<doctrine-mapping>
FilterOption.orm.xml:
<doctrine-mapping>
<entity name="FilterOption" table="filter_option">
<id name="id" column="id" type="integer">
<generator strategy="AUTO" />
</id>
<field name="text" column="text"/>
<many-to-one field="filter" target-entity="Filter" inversed-by="options">
<join-column name="filter_id" referenced-column-name="id" />
</many-to-one>
</entity>
<doctrine-mapping>

Doctrine Optimistic Locking

I am having a problem with doctrine optimistic locking.
I have mapped my entity like the documentation says (I guess I think so), and my optimistic locking version doesn't increment. It always stays as null inside the database.
Part of my entity and it's mapping:
Entity:
/** #var AccountID */
private $accountID;
/** #var ClientID */
private $clientID;
/** #var Money */
private $money;
/** #var array|Transfer[] */
private $transfers;
/** #var bool Account status - active not active */
private $active;
/** #var int */
private $version = 1;
Mapping:
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">
<entity name="Madkom\ES\Banking\Domain\Account\Account" table="account">
<indexes>
<index name="client_id_idx" columns="client_id"/>
</indexes>
<embedded name="accountID" class="Madkom\ES\Banking\Domain\Account\AccountID" />
<embedded name="clientID" class="Madkom\ES\Banking\Domain\Account\ClientID" />
<embedded name="money" class="Madkom\ES\Banking\Domain\Money" />
<field name="transfers" column="transfers" type="jsonb"/>
<field name="active" column="active" type="boolean" length="32" unique="true" />
<field name="version" type="integer" version="true"/>
</entity>
Whenever I added new record, version is always the same, null.
I am using Postgresql 9.4 and Doctrine 2.5.1.
The jsonb type comes from outside library.

How to build zend 2 forms automatically from doctrine 2 entity-classes?

Is there a way to automatically build a zend framework form by having a doctrine 2 entity-class?
Important Information: This is only a example and no productive setup.
E.g.
Users (Entity) class:
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*/
class Users
{
/**
* #var string
*/
private $name;
/**
* #var integer
*/
private $id;
/**
* Set name
*
* #param string $name
* #return Users
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
Users xml mapping definition:
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Users" table="users">
<id name="id" type="integer" column="ID">
<generator strategy="IDENTITY"/>
</id>
<field name="name" type="string" column="name" length="50" nullable="true"/>
</entity>
</doctrine-mapping>
Now i would like to have a zend 2 form class, where i can inject this entity and the form itself builds a basic form and attaches validators to this form. Here in this example it would atach a StringLenght validator (max 50; see xml mapping above) to the users field.
Something like this is possible like i know in CakePHP. I would be really happy to have such feature in Zend framework 2, too;-)
For now i found one link: http://www.lewanscheck.de/2013/09/21/inject-doctrine-orm-entitymanager-in-zf2-form/ but no automatic creation of validation from the xml file.
Would be great to implement only a model/entity class with a xml/yml/annotation(even when i do not like annotations) configuration and build automatically a db schema-part (this works with doctrine 2) and a form class.

Doctrine mapping a single entity class to multiple tables without inheritance

Is it possible somehow to map a single entity class to two different tables without inheritance?
Something like:
<entity name="Acme\Entity\Service" table="organization_service">
...
<many-to-one field="target" target-entity="Acme\Entity\Organization" inversed-by="services">
<join-columns>
<join-column name="target_id" referenced-column-name="id" />
</join-columns>
</many-to-one>
</entity>
<entity name="Acme\Entity\Service" table="person_service">
...
<many-to-one field="target" target-entity="Acme\Entity\Person" inversed-by="services">
<join-columns>
<join-column name="target_id" referenced-column-name="id" />
</join-columns>
</many-to-one>
</entity>
where entity classes could look like:
class Service
{
...
/**
* #var Person|Organization
*/
private $target;
}
class Person
{
...
/**
* #var Service[]
*/
private $services;
}
class Organization
{
...
/**
* #var Service[]
*/
private $services;
}
A workaround I've found so far is to create two classes PersonService and OrganizationService with the same content and map them to "person_service" and "organization_service" tables accordingly.

Categories