I am working with Symfony2 and Doctrine ORM and want to achieve the following with a clean architecture :
Each time a new Entity is created, I want to save a "display name" chosen by my end-user, then generate a "unique name" based on the "display name".
If my end-user want to create 3 Project called "Drawings",
the first one will have display_name = "drawings"
the second one will have display_name = "drawings2"
the third one will have display_name = "drawings3"
(or something like that, whatever the pattern)
Basic Entity example :
/**
* Project.
*
* #ORM\Entity
*/
class Project
{
//...
/**
* #ORM\Column(type="string", length=50, nullable=false)
*/
protected $name_display ;
/**
* #ORM\Column(type="string", length=50, nullable=false, unique=true)
*/
protected $name_unique ;
//...
Basic usage example :
$project = new Project();
$project->setDisplayName('Drawings');
//Around here I would like the Unique name to be generated
$this->getDoctrine()->getManager()->persist($project);
I thought about various solutions :
Doing the name generation in the Controller, but it's not re-usable
Doing the unique name generation in the repository. But it seem to be a bad practive (repositories should be read-only)
Using a PrePersist LifecycleCallbacks from doctrine, but it's not a good practice as I need the Entity Manager to do a Database
Doing the name generation in the Entity Setter, injecting the Entity Manager to make requests and look for available names. That looks horrible
Using a service to persist the Entity as explained here : Are Doctrine2 repositories a good place to save my entities? (But it's quite complicated and involve a huge change in my infrastructure if I want to have all my Entity creations to be consistent with this practice)
I would recommend the last options - services. It may need changes in your project, but I find this the best way to manage usual crud operations with entities - create, save, findBySomething ...
It is crystal clear - no black magic. As opposed to events where there is no obvious relation between the executed code and actions with entities (like creating it through new).
It is not dependent on annotations and it is easy to maintain.
Controllers and other services may access this service through Dependency Injection which is a clear way of satisfying dependencies of business objects(objects holding business logic)
Your repositories won't become bigger and bigger
You can use default repositories - fewer issues with back compatibility when upgrading Doctrine
It is much better than the "setter solution", which sounds really horrible - entities should never be that mighty, so they would have references to services (especially services like EntityManager)
Related
I have two entities linked together by a ManyToMany relationship in a Doctrine/MySQL project.
A Client entity:
class Client
{
[...]
/**
* #ORM\ManyToMany(targetEntity="ClientTag")
* #ORM\JoinTable(name="clients_tags")
*/
protected $tags;
}
And a ClientTag entity:
class ClientTag
{
[...]
/**
* #ORM\Column(type="string", length=45)
*/
protected $label;
/**
* #ORM\Column(type="string", length=7)
*/
protected $color;
}
So I have the ability to associate multiple clients to one tag, and vice-versa, great.
But I can't find a way to automatically remove a tag when there is no more clients referencing it.
I tried to use orphanRemoval on the ManyToMany annotation but it doesn't do what I thought.. Orphan removal should imply exactly what I described above but it removes the tag when the reference to its parent is removed, not considering other entities like I need to.
If a client removes a tag but this tag is still used by 2 other clients, I don't consider it "orphan" as it still has one or more entities referencing it.
Of course I could solve the case by doing a query and removing it myself if I don't find any parent, but I wonder if Doctrine or MySQL have a built in way to do this (that will be far more optimized) ?
Any idea?
Thanks for your help.
Officially orphanRemoval isn't supported for ManyToMany relations in doctrine.
http://docs.doctrine-project.org/en/latest/reference/annotations-reference.html#annref-manytomany
The orphan removal in this case is ambiguous.
You can either just understand the relations (the jointable entries) to the deleted entity as the orphans or the related entity.
From a database point of view it would be the jointable entries.
From an ORM point of view it's the related entities.
Thing is both ways are correct depending on the use case. For example in an Article <-> Category relation you would want to remove the article from all associated categories on deletion, but you wouldn't want to throw away the whole category just because it's empty at this moment.
I'm guessing that's the reason why Doctrine doesn't officially mention the orphanRemoval option for ManyToMany because it's unclear and to fully support both variants the current implementation isn't enough.
Hope that was somehow understandable.
In your case though you'll probably need to clean up unused tags yourself.
I'm starting a little project with DDD approach. I've created my domain model with Entities and ValueObjects in plain PHP. Entities have references to their associations - in my case, there is an Employee entity with collection of Teams he belongs to, and I keep them in Employee::teams property. Everything is going great, I've created mappings for those entities with associations in YAML, interfaces for repositories to be implemented in the Symfony2 and Doctrine2 layer, etc.
When I fetch Employees from repository (with Doctrine's EntityManager::findAll()) instead of array of Teams I receive PersistentCollection with those teams. It's built on PHP7, and Employee::getTeams() has return type of array so I'm getting the critical exception.
Is there any way to convert it into array with some external listeners (Symfony layer only, to not to mess in domain files) or any other core mechanism?
Thanks.
You get ArrayCollection
http://www.doctrine-project.org/api/common/2.5/class-Doctrine.Common.Collections.ArrayCollection.html
It has toArray() method so you can write
/**
* #return Team[]
*/
public function getTeams(): array
{
return $this->teams->toArray();
}
But I prefer
/**
* #return Team[]|ArrayCollection
*/
public function getTeams()
{
return $this->teams;
}
If you use good IDE like PhpStorm it will understand it correctly to autocomplete it like ArrayCollection and as Team if you make foreach ($employee->getTeams() as $team) {...
ArrayCollection are more powerful than plain arrays. For example you can filter and order them and Doctrine will make optimized SQL so you don't have to load all items
http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html#filtering-collections
Kuba,
That doesn't seem to be a DDD question but a technical question regarding PHP implementation details.
However, please let me jump into your design and try to find a better solution for your domain. You are missing to model the relation between an employee and a team, let's call it employee in team. The way you did you are forcing an AR to reference another AR, saying this way that employee manages a team life cycle. This might not be the case but under a specific scenario another actor might change a status of a team and that could break the Employee invariant over the Team AR. Because of this when you make an AR to manage another AR life cycle then this dependency shouldn't be found through the repository and the root of the aggregate is in the position to now force invariant around the aggregated AR.
There are still some concerns to mention, but just to keep it simple, go ahead and model the concept you are missing: EmployeeInTeam or whatever you want to call it. It references the conceptual identity of the employee and the team so you can remove it safely in a future, you can query employees in a team and teams an employee is part of.
If you need frameworks or the DB to keep the consistence then you are not doing DDD. Use just objects, not the technology.
Regards,
Sebastian.
When creating a bundle which is not for one application only, but thought for a more generic use, lets say a ChatBundle, I always hit the same problem about how to use an user object which of course is needed in many cases (like in a chat).
In a bundle which is only used in one application, I simply hard code a reference to my main bundle like this:
<?php
namespace Acme\ChatBundle\Entity;
/**
* #ORM\Entity
*/
class Message
{
/**
* #ORM\ManyToOne(targetEntity="Acme\ProjectBundle\User")
*/
private $user;
...
But in a generict bundle this isn't possible. Is the only way to let the developer implement all entities on his own? Are there any best practices? I could not find any documentation or blog posts about that topic, except the code of other bundles (but most of them are that complex and abstracted that it's hard to grasp the main issue there).
I think the best solution here is to use Doctrine's TargetEntityListener
I am trying to figure out a smart way to implement my bundles with following requirements:
I have a Bundle with logic named LogicABundle
I have a Bundle with common things as design and Menus called
AppBundle
I have another Bundle with logic LogicBBundle with some entities
related to LogicABundle entities
I know want to be able to "deploy" two applications from this setup:
Application one uses the LogicABundle and AppBundle
The second one uses LogicABundle, LogicBBundle and AppBundle
The issue is, that for the second application I need to relate some Entities from LogicABundle to LogicBBundle, which causes the first "deploy" option to brake, if I just have an entity in LogicABundle pointing to LogicBBundle.
Is there a smart solution to deploy these two different applications independently?
Here is an example in order to make it easier to understand:
namespace My\LogicABundle\Entity\Game;
use Doctrine\ORM\Mapping as ORM;
/**
* My\LogicABundle\Entity\Game
*
* #ORM\Entity
*
*/
class Game
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $title
*
* #ORM\Column(name="title", type="string")
*/
private $title;
/**
*
* #var Message
* #ORM\ManyToOne(targetEntity="\My\LogicBBundle\Entity\Message", inversedBy="games")
* #ORM\JoinColumn(name="messag_id", referencedColumnName="id", nullable=false)
* #Assert\NotNull()
*/
private $message;
}
I want to be able to use the Game class in my standalone application only with LogicABundle, and in my second application I need the game Entity with message relation.
I am not sure, but I have the same problem and I just found that : http://symfony.com/en/doc/current/cookbook/doctrine/resolve_target_entity.html
Hope not to late ;)
If you are using Git (or SVN or another source countrol tool) I would recommend to create two separate Symfony2 applications (each in its on repository). Also, every bundle gets its own repository and I would use Composer to set up the dependencies correctly and then install the bundles (LogicABundle, LogicBBundle, AppBundle) as vendors.
Update: Since the different bundles need different entities, one way is to specify the base entity in the bundle that does only need the base entity and extend the entity in the other bundle with additional relations (see Doctrine Inheritance Mapping).
For example, define EntityA in LogicABundle and define EntityA2 in LogicBBundle where EntityA2 extends EntityA and adds additional relations to the entity.
Update: Since you do not have provided additional information why you need to do this, I can only guess now, but one additional idea would be to simply use the same entities in both bundles. The logic in LogicABundle would simply ignore the additional relations. I think that is what most developers would do in your situation.
Consider, for example, bundles that provide common functionality like FOSUserBundle. The bundle defines some models, but not every application that uses FOSUserBundle has to use every field of the entities (in a application I am currently developing I completely ignore the groups functionality of FOSUserBundle).
Please provider further information if it is possible to use a common entity class and ignore these additional relations.
I've decided that it would be a good asset to get familiar with an ORM and went for Doctrine 2 as the ORM of choice.
I'm working on a test project to learn the basics of Doctrine. Although most people usually go with a blog, I've decided to make a basic app in which you can save and track orders. My database schema would be as follows:
User
id
name
Product
id
name
price
Sales_order
id
user_id
product_id
quantity
unit_price
Hence, my Order model looks like:
/**
* #Entity
* #Table(name="sales_order")
*/
class Order {
/**
* #Id
* #Column(type="integer", nullable=false)
* #GeneratedValue(strategy="AUTO")
*/
private $Id;
/**
* #OneToOne(targetEntity="User", inversedBy="user")
*/
private $user;
/**
* #OneToOne(targetEntity="Product", inversedBy="product")
*/
private $product;
/**
* #Column(type="integer", nullable=false)
*/
private $quantity;
}
Now, the question is, is there a simple way of accessing all the orders from the user model? Should I write DQL (doctrine query language) for these kind of basic stuff or is there a way to easily get associated entities? I mean, there wouldn't be any point to this otherwise, right? Also, am I doing these associations correctly? I'm really confused in this very basic model... Detailed help is really appreciated. Thank you.
Firstly, don't worry too much about the database design. You should design your entities and use the SchemaTool.
Now, the question is, is there a simple way of accessing all the orders from the user model?
Do you mean access all of the orders from the user model, or access all the orders associated to a user?
If you meant the former, well you are doing things wrong (see below). If you meant the later, you should setup a bi-directional relationship between orders and users. (BTW, it would be OneToMany not OneToOne as one user would likely have many orders).
I'm really confused in this very basic model...
I think what you having trouble with - along with many PHP programmers - is the fundamental understandings of the DataMapper pattern and ultimately Domain Driven Design as well. Remember, you are dealing with persistable objects, not database tables.
I cannot provide detailed information here because I'd be writing a book, hence this I would recommend you get a book on Domain Driven Design to help kick start with the principles. There are a few good online resources available, like a series of blog posts by Federico Cargnelutti, however they aren't specific to Doctrine 2.