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.
Related
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)
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.
I'm in the process of learning OOPHP, and I'm looking for some 'best practice' advice.
With a relational db, there's obviously foreign keys in many tables. When I am creating Models for my site I am trying to determine if it is better to do JOIN queries in the model, or have one model call another?
Calling other models seems to keep the code more modular. Either way seem to create dependencies, whether it be on another table or anther model.
If I go with the 'call other models' approach I seem to run into another problem: infinite loops. Here's the example I'm running into. I have 2 tables, person and school. Each person has a favorite school, represented by a schoolId. Each school has a principal, that is a personId.
The person object the row is mapped to accepts a school object in its constructor, but the school object the school row is mapped to accepts a person object in its constructor.
From what I've found, something about lazy loading, seems to be the solution, but (I could be wrong) it seems if I do that I can't use PHP's type hinting.
(I'm guessing many will suggest an ORM tool like Doctrine to me, and it's something I will definitely look into in the future. I am avoiding it now because of its supposed steep learning curve, and because I feel I understand those tools better later on if I try it myself once)
Nice question! I had/have same thoughts. And I think it's a really good idea to start code your own before looking at frameworks :)
When I coded my model generator I decided once not to use JOINS and use 'lazy loading'. Meaning I have classes like this (just pseudo code):
class Person extends Model_Resource {
/**
*
*/
protected $name;
/**
* #var Address <-- external reference
*/
protected $address;
/**
* #return Person
*/
public static function select($filter) {
return DB::instance(filter_query('SELECT * FROM `person`', $filter));
}
/**
* #return Address
*/
public function getAddress() {
// lazy loading :
if(is_null($this->address)) {
$this->address = Address::select(new Filter('p_id', $this->id));
}
return $this->address;
}
}
That worked good for me in a medium size application over years. When I really need to speed up things or using a somewhat inregular query, then I'm free to overwrite the auto generated methods and use JOINS, UNION etc...
I'm curious what will others say.
I have these two entities:
Message entity
class Message
{
/**
* #ManyToOne(targetEntity="User")
* #JoinColumn(name="author", referencedColumnName="id_user")
*/
protected $author;
User entity
class User
{
/**
* #Id
* #Column(type="integer", nullable=false, name="id_user")
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Column(type="string", nullable=false)
*/
protected $name;
I need to get the total of messages of user and its data in order to echo something like this:
echo $user->getName() . " have {$user->totalOfMessage()}";
I now that I can make a relation in User entity to get a message collection. But I don't know if it necessary only to get the size.
Well, I found the answer. At least one good option.
As of Doctrine 2.1 you can mark associations as extra lazy. This means that calling $user->getMessages()->count() won't load the messages, it will just issue a COUNT query to the database.
You can read about extra lazy collections here: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/tutorials/extra-lazy-associations.html
I've recently been trying to get the EXTRA_LAZY loading working and found what I think is quite a major mistake in the documentation as shown on page:-
http://www.doctrine-project.org/docs/orm/2.1/en/tutorials/extra-lazy-associations.html
I expect that most Doctrine users may realise that the fetch="EXTRA_LAZY" needs to be put on the 'owning' side of the ManyToMany relationship but the example shows the annotation being added to a line which contains 'mappedBy' which is NOT the owning side?
By adding the fetch="EXTRA_LAZY" directive to the non-owning side it gets completely ignored as I now know it should be. I think this should be more clearly stated in the documentation.
I could not find any article anywhere explaining this after exhaustive searching so I'd say this is probably worth an amendment to the docs?
In our use case it's made a particularly intensive block of processing which used quite a number of 'contains' calls and which constantly crashed due to exceeding memory limits into a quick process which now executes orders of magnitude faster.
Hope this info helps someone out there...
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...