Why Doctrine 2 not have basic validate method that validate if all values fit entities attributes? - orm

I am new with doctrine 2.
Why Doctrine 2 not have basic validate method that validate if all values fit entities attributes?
My question target to understand more how doctrine 2 works and why without say that something wrong in doctine 2. (Mostly because i am new i miss some understanding about doctrine 2 way of design)
Example:
<?php
// entities/User.php
/**
* #Entity #Table(name="users")
**/
class User
{
/**
* #Id #GeneratedValue #Column(type="integer")
* #var int
**/
protected $id;
/**
* #Column(type="string")
* #var string
**/
protected $name;
}
code example of use of build in validate(not need connect to db, only validate #Column(type="integer") ) basic function that not exist in doctrine 2:
$user=new User();
$user->setId('trtr');
$user->setName("goodname");
if($user->validate()){
echo 'ok';
}
else{
echo $user->validateError();
}
//output: id of User should be integer and not string
Thanks

Doctrine ORM assumes that entities you're persisting are in a valid state. That's the only job of the persistence layer, and adding validation to it would just be wrong. If you have entities with invalid data in them, you already have an invalid object graph that should not be saved.
So please keep in mind that if you ever had some API like
$someEntity->isValid();
Then something is probably wrong, since the entity should always be valid, and any dependencies of it should be set at construction time and handled in setters/getters so that the object never reaches an inconsistent state.

The main reason is separation of concerns. Since entities are fairly dumb objects that don't know much about the rest of the world, their ability to do validations is limited to begin with.
For instance, there's no way that your typical entity could validate that a particular property is unique.
That said, if you just want to do basic validations, just do them in the setters.
<?php
class MyEntity {
// ...
/**
* #ORM\Column(length="32")
*/
protected $myProperty;
public function setMyProperty($prop){
if (! is_string($prop))
throw new \InvalidArgumentException('MyEntity::setMyProperty() expects a string!';
if (strlen($prop) > 32)
throw new \LengthException('Argument passed to MyEntity::setMyProperty() is too long!');
$this->myProperty = $prop;
}
}
This approach can be used to enforce data types, lengths, etc. Anything beyond that is better handled somewhere other than inside your entity.

It's not good idea to mix entity and validation, but it make sense to have this rules in entity as annotation and validation logic in separated aspect validator class.
Check how it's done in Spring framework -
http://www.abtosoftware.com/blog/form-validation-using-aspect-oriented-programming-aop-in-spring-framework
and how to implement it with doctrine2 and go -
http://go.aopphp.com/blog/2013/07/21/implementing-logging-aspect-with-doctrine-annotations/

Related

JMS VirtualProperty with Param

I have an API backend project build with symfony 2.8, doctrine and JSM Serializer. I have an m-m relation, beetween user and book.
What i want is, when i serilize all my book, have an virtualProperty that told me if a user have a book.
So i need to pass to the funcion of book entity isPossesed() the user object, so i can see if that user is in array of users that have that book.
The problem is that when i serialize with JSM i don't know how pass a param the the method.
Somethings like this:
/**
* #Serializer\VirtualProperty()
*/
public function isPossessed(User $user)
{
/* some code
*/
}
Someone can help me?

DDD - Association mapping between bounded contexts using Doctrine 2

I am struggling to understand the right way to implement association mapping between two entities from different bounded contexts using Doctrine 2. Suppose that there are two "User" and "Post" entities that belong to "User" and "Content" bounded contexts, respectively. There is also a "User" concept in "Content" context that determines the author of a "Post" through a Many-To-One association. Therefore, "User" in "Content" context is simply a value object containing the user id.
My question is that how should I implement this association using Doctrine 2? I have two solutions that both have their own issues:
Solution 1:
/**
* #ORM\Entity
* #ORM\Table(name="posts")
* #ORM\HasLifecycleCallbacks()
*/
class Post
{
...
/**
* #ORM\ManyToOne(targetEntity="UserBC\User", inversedBy="posts")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
...
}
Solution 2:
/**
* #ORM\Entity
* #ORM\Table(name="posts")
* #ORM\HasLifecycleCallbacks()
*/
class Post
{
...
/**
* #ORM\Column(type="integer")
*/
private $user_id;
...
}
In the first solution, "User" entity from "User" context has been used inside "Content" context that violates DDD rules on BCs being independent of each other. The second solution respects DDD rules but affects database schema (removes database-level relationship between "users" and "posts" tables through a Foreign key constraint).
So, what is the right way to implement such associations?
The second solution is correct.
As you correctly observe, associations between different BCs should be avoided. The right way to reference an entity in another BC is by ID.
This has the consequence that the BCs don't have constraints between them in the DB. After all, you try to make them independent. If you feel that this is wrong, then the only way around this is to reconsider your BC design, i.e. merge the two BCs. This is however a decision that should not be driven by code smells, but by your context map.
Note: Referencing entities from other BCs by ID is only allowed if they are aggregate roots. If the referenced entity is not an AR, you have another design smell right there. Not a serious one, but still one that needs consideration.
Also for me the second solution is the correct one. And I try to answer to the #EresDev question:
"In solution 2, how do you know that $ user_id is correct and already exists?"
To do this you should use events. For example you could dispatch a PostPublicationEvent which contains the user id as well as post data. This event is listened by the User BC. Here you can verify that the user exists and dispatch a new UserValidatedEvent which is listened to by the Post BC. Now you can publish your post knowing that the user is valid.

Doctrine2, create one entity from another?

I'm using Doctine2\ORM and i have en entity for user , and the role.
When user is registering, i need to create role record with user id and return its id to user, then create the user record, how can i organize my annotation for such work?
You can add a default role to your user during __construct, then mark the association as cascade={"persist"}.
The constructor would look like:
public function __construct()
{
$this->role = new RoleLink();
}
I also don't think that a role needs to keep a reference to the user itself, but if need that, keep in mind that in Doctrine 2 ORM you handle associations by assigning related objects to the association property itself (not identifiers!)
Firstly, I don't think you want to create your role entry when a user is registering. I think it's wise to define these upfront (along with their respective access rights).
Once you've taken care of that I guess the obvious relation you're after would be ManyToOne on the User with a persistence cascade set.
/**
* #Table(name="role")
*/
class Role
{
// possibly define your roles accesses as another realtion or hard code?
}
/**
* #Table(name="user")
*/
class User
{
// id + any other definitions
/**
* #var Entities\Role $role
* #ManyToOne(targetEntity="Role", cascade={"persist"})
*/
private $role;
}

Doctrine 2 ORM and MongoDB ODM mapping on the same class

Is it possible to map the same class to both ORM and ODM using annotations?
We are deciding what to use so we want to do some performance measurment and we also want to be able to switch the persistance mappers easily. I have already done the manager unification, now i would like to unify the classes. Now i have a copy of each class in separate namespaces for Entities and Documents which i find kind of redundant.
I read this article http://www.doctrine-project.org/docs/mongodb_odm/1.0/en/cookbook/mapping-classes-to-orm-and-odm.html, but in the end i guess they use two different classes, each in their own namespace.
Has anybody tried this?
I've never tried but it's totally possible to put both ODM and ORM mapping on the exact same class.
The problem will maybe rely on synchronization of data between these two persistence backends
and the Entity API. For example, if you have a ManyToOne association, ODM will have a different internal in memory reference than ORM. So it's possible that it will override objects you were working with.
Didn't try this before but if i could suggest something is to have different mapping in xml/yml for you entity/document class?
Yes you can. I have done it using symfony and annotations, so I guess you could manage just as well using whatever environment you are using.
First, I added both annotations on the entity:
<?php
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
/**
* User
*
* #ORM\Entity
* #ODM\Document
*/
class User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ODM\Field()
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=200, nullable=false)
* #ODM\Field()
*/
private $email;
}
Under symfony the default directory for ORM is the Entity directory, for ODM the default directory is Document. So if you have an entity that has to be a document at the same time, you have to configure either of the two mappings manually.
doctrine_mongodb:
document_managers:
default:
mappings:
# Default mapping for the bundle (loads Document/)
DemoBundle: ~
# Extra mapping to load document mappings under Entity/
DualMappingHack:
type: annotation
dir: %kernel.root_dir%/../src/Acme/DemoBundle/Entity
prefix: EntityPrefix
is_bundle: false

Mapping two tables to one entity in Doctrine2

I'm looking at using doctrine for an application I'm working on - but after reading the documentation I'm having trouble conceptualizing how to represent the database structure we have in terms of entities.
I have many tables which have partner tables which hold translation data like the following....
Where I would like to have one Entity (Navigation Element) which had access to the 'label' field depending on what Language I set in my application. The following from the Doctrine documentation seems to suggest that you need to define one (single) table which is used to persist an entity
http://www.doctrine-project.org/docs/orm/2.0/en/reference/basic-mapping.html
By default, the entity will be
persisted to a table with the same
name as the class name. In order to
change that, you can use the #Table
annotation as follows:
Or do I need to define two entities and link them (or allow the translation table to inherit from the element table).
And what strategy would I use to always insert a language_id clause to the Join (to ensure I'm pulling the right label for the currently set language). Is this something I would define in the entity itself, or elsewhere?
This seems to suit a One-To-Many Bidirectional association. This is the scenario from that page translated to your situation:
/** #Entity */
class NavigationElement
{
// ...
/**
* #OneToMany(targetEntity="NavigationElementTranslation", mappedBy="navigationElement")
*/
private $translations;
// ...
public function __construct() {
$this->translations = new \Doctrine\Common\Collections\ArrayCollection();
}
}
/** #Entity */
class NavigationElementTranslation
{
// ...
/**
* #ManyToOne(targetEntity="NavigationElement", inversedBy="translations")
* #JoinColumn(name="navigation_element_id", referencedColumnName="id")
*/
private $navigationElement;
// ...
}
You could add a getLabel($languageId) method to the NavigationElement entity that searches through the translations to get the correct label:
public function getLabel($languageId) {
foreach($this->translations as $trans) {
if($trans->languageId == $languageId)
return $trans->label;
}
throw new InvalidArgumentException();
}
And you could use the following DQL to ensure you only load the translation you want into the $translations property:
$query = $em->createQuery(
"SELECT ne, net
FROM Entity\NavigationElement ne
JOIN ne.translations net WITH net.languageId = :langId"
);
$query->setParameter('langId', $languageId);
$navigationElements = $query->execute();
This situation sounds like one where you would want to cache aggressively. Make sure you look into Doctrine 2's caching mechanisms too.
Also, internationalization can be handled reasonably well in PHP with gettext if you find join tables for translations start to become unmanageable.
I would also direct anyone who has to tackle this same problem to take a look at the following doctrine extension.
http://www.gediminasm.org/article/translatable-behavior-extension-for-doctrine-2