How to setup Symfony2 Bundles independently with related Entities - php

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.

Related

Adding properties to existing doctrine entities

I have the following design problem: I have two projects which both use doctrine to manage their entities. Both projects work on similar entities (most of them are in fact equal). I thought now it would be the best to write a library which contains the core entities used by both projects.
But this gives me more headaches than I first thought. Lets give you the following example, I have an entity
class Entity {
... a lot of managed properties used in both projects
}
then in project A i use exactly this entity, in project B my entity looks a little bit different:
class Entity {
... a lot of managed properties used in both projects
... one managed property only used in project B
}
Now if I declare Entity in my library I could extend it in my project B to add the additional property. But then the question is how to do it with doctrine. If I declare the entity in the library managed I can't easily declare the derived class as managed too in project B. On the other hand if I don't declare it managed in the library I won't be able to have associations containing the entity (which is in my case very important, I have a lot of associations across all my entities).
How should I handle such a situation? Writing the whole entity structure two times in both projects? It doesn't seem the right thing to do for me, but I can't think of another solution.
You could consider using traits that would contain sets of annotated properties and their related methods. This would give you the possibility to have a common base on which you can build both sides independently.
The down side is that both objects would not have a common interface, some of their internals would be the same but you would not be able to tell from the outside. A way to go around this would be to write an interface that exposes the methods you need to operate with those entities in your common library. The common trait could then take care of implementing that interface.
Edit: As you pointed out, traits are not very helpful if we are dealing with associations as you cannot override the targetEntity of an association with AssociationOverride annotation. That being said, by searching about that particular topic, I stumbled on the ResolveTargetEntityListener which might be the tool you were searching for.
The official documentation has a detailed page about how to use it (if you are using Symfony, there is this article as well). As explained in the documentation, thanks to that utility, you can have relationships between entities based on abstract classes or interfaces that would be resolved at runtime.
ResolveTargetEntityListener use with abstract classes
Here is a working example with abstract classes with the annotation MappedSuperClass:
AppBundle/Model/AbstractA.php
use Doctrine\ORM\Mapping as ORM;
/** #ORM\MappedSuperclass */
abstract class AbstractA
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="text", type="string")
*/
private $text;
/** #ORM\ManyToOne(targetEntity="AppBundle\Model\AbstractB") */
private $b;
}
AppBundle/Model/AbstractB.php
/** #ORM\MappedSuperclass */
abstract class AbstractB
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/** #ORM\Column(name="number", type="integer") */
private $number;
}
AppBundle/Entity/A.php
/** #ORM\Entity() */
class A extends AbstractA {}
AppBundle/Entity/B.php
/** #ORM\Entity() */
class B extends AbstractB {}
And since I tested this with Symfony, I had to add the following in my config:
doctrine:
orm:
resolve_target_entities:
AppBundle\Model\AbstractA: AppBundle\Entity\A
AppBundle\Model\AbstractB: AppBundle\Entity\B
When asking doctrine for a schema update, it outputs:
CREATE TABLE a (id INT AUTO_INCREMENT NOT NULL, b_id INT DEFAULT NULL, text VARCHAR(255) NOT NULL, INDEX IDX_E8B7BE43296BFCB6 (b_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
CREATE TABLE b (id INT AUTO_INCREMENT NOT NULL, number INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
ALTER TABLE a ADD CONSTRAINT FK_E8B7BE43296BFCB6 FOREIGN KEY (b_id) REFERENCES b (id);
That is a common and actually really good question. Lots of people are in trouble with such issues, and we cannot say that there is one and only one right answer to that.
I experienced various solutions, and the best I could "use" is to duplicate the entity(ies) definition(s) in each project.
Why ?
Because it is indeed easier to maintain, and even if you have some migration strategies (e.g. with Doctrine migrations or whatever), your code, relying on a specific version of the entities, could break.
You can try to deal with that kind of issues with a tool for migration strategies (i'm kinda out of the subject), and I'd then recommend to store every property (even the ones that are project-specific) in your library.
But do this if you really need it. Is it a big deal for you to duplicate ? Sometimes, factoring everything is not a good idea.
If you duplicate the definitions, you'll also be able to only focus on properties that you really care about. This means that some properties that you don't care about can be omitted from your entities definition in each project.
Just ensure that you still have a strategy for your migrations, it's always a good idea.

Design pattern Doctrine ORM - Generate unique names based on input

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)

Doctrine 2 - Relate from abstract class to abstract class

Alright. This question is kind of hard to describe. But here goes. I'll post some images first, just incase someone gets what I'm doing from this image;
A Block is an element that can be used to fill a webpage or blogpost. This can be images, text or forms. These Blocks are ContentBlocks. Block has a DiscriminatorColumn and DiscriminatorMap properties that are used to join the right Block table and create the underlying Block element. (i.e. an ImageContentBlock)
On the other hand we have Forms. Forms consist of FormBlocks. These are certain common Form elements. (TextField, PhoneField etc).
I want to be able to relate to the Content- or FormBlocks from either Page, Post or Form.
How can I achieve this in Doctrine?
I could add an entityType and entityId field to the Block class. But that would remove the object orientated style of programming. I would rather refer to the owning ContentEntity. But then again. I need to join or relate to the Blocks.
Not every ContentEntity has Blocks. So I cannot add this as an property of ContentEntity.
Now. I could off course use a ManyToMany relationship and go with a JoinTable. I guess that would always work. But I would have to join twice.
I think your problem isn't primarily about the data relations but about the fact that you want to avoid duplicate code. This results in your “Entity” being at the top of your hierarchy, only because it has a few common properties that each entity should have. (By the way, naming an entity “Entity” is a bit confusing.)
Maybe what you're looking for are Traits. So, instead of providing id and active through an entity, it could as well be a trait:
trait CmsEntity
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\Column(type="boolean")
*/
protected $active;
// add getters/setters and other code as you like
}
Now you can attach this trait to all the entities which should have the given properties.
/**
* #ORM\Entity
*/
class Page
{
use CmsEntity; // import the trait
/**
* #ORM\Column(type="text")
*/
private $header;
// etc.
}
This will make you free from the requirement of deriving all your entities from one master “Entity” which just carries some common properties.
And now you can create a direct relation between between “ContentEntity” and “Block” (1:n I'd guess), which is more logical.
There's also a nice article elaborating on using Doctrine with traits for further reading.

Doctrine possible to reverse engineer inverse associations?

I have database with about 100 tables and I'm using Doctrine 2 as my Data Mapper. I successfully generated entities for my tables, however, I noticed that many-to-one relationships didn't generate bidirectionally. Only many-to-one part of the relation generates, the one-to-many does not.
For instance in my Company entity I have
/**
* #var \User
*
* #ManyToOne(targetEntity="User")
* #JoinColumns({
* #JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
private $user;
but I don't have anything pointing to the Company entity in User.
I am aware that Doctrine doesn't do this OOTB it says so in their documentation but I was wondering if there is a way to get around this limitation.
Writing 300+ relations by hand is a task I don't want to undertake.
Is there perhaps an alternative Data Mapper library for PHP that can solve this for me?
Thanks in advance.
I ended up using Propel because it generated everything wonderfully, albeit I ended up with some very large files (14k LoC).
It seems there simply isn't a PHP ORM that does everything right.

Doctrine : One to one to one relation?

I'm making entities for a Symfony2 project at work.
I'm trying to make a system that controls the access to certain resources in function of an organisation (a company) and of a role. To sum it up, roles are the same for all the companies, but a company may make a resource available for a role, as another may not want to.
As for resources, they represent some actions and contents, such as the creation of this, the edition of that, and so on...
I attempted to solve this problematic with the following entity. It represents a one to one to one relationship between my three entities Organisation, Role and Resource.
I wanted to know if that kind of relation was possible/good, or if there is another way to manage resources.
/**
* #ORM\Entity
*/
class Organisation_Role_Resource
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Aurae\UserBundle\Entity\Organisation")
*/
private $organisation;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Aurae\UserBundle\Entity\Role")
*/
private $role;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Aurae\UserBundle\Entity\Resource")
*/
private $resource;
Do you have any piece of advice on how to solve this problem?
Is there another/better way to represent resources (which are, in fact, pages and links) and to manage their access?
While this might be quite valid approach you would be really reinventing the wheel.
Symfony2 has it all implemented already as 'Access Control Lists' or (ACL):
http://symfony.com/doc/current/cookbook/security/acl.html
Check it out.... I think it covers everything you need...

Categories