Eloquent è uno strumento potentissimo: permette di velocizzare, e di molto, lo sviluppo di applicazioni web offrendoti un’interfaccia molto chiara e intuitiva. Per la prototipazione rapida è il massimo. Tuttavia, non appena l’applicazione cresce di dimensione, la situazione rischia di esploderci in mano in quanto Eloquent soffre di tutti i limiti di un Active Record. Un’alternativa a questo pattern è il Data Mapper, implementato in maniera eccellente da Doctrine. In questo talk vedremo quindi come integrare Doctrine all'interno di Laravel e verrà dato qualche spunto su come strutturare un’architettura molto più solida.
5. Problem:
// Data could be invalid
$user->email = ‘that’s not a valid email’;
$user->save();
Nothing prevents inconsistency
Bugs alert!
6. Problem:
- No property hinting on your IDE
- No static code analysis => No typo prevention
- Magic properties => Only manual refactor is possible
Error raised only at runtime!
Bugs alert!
$user->usermane = “christian.nastasi”;
$user->pasword = “that’s a secret”;
7. Problem:
- Violates SRP (Single Responsability Principle)
○ wraps a row in a database table or view
○ encapsulates the database access
○ adds domain logic on that data
● Test them is complex, really
○ It’s too close to the infrastructure, you can’t unit testing
your application without a real Database.
○ You have to drop, migrate and seeds for every single test.
That’s time expensive and complex to manage.
9. Active Record Pattern
“Active Record is a good choice for domain logic that isn’t
too complex, such as creates, reads, updates, and deletes.”
Martin Fowler
11. What’s a Data Mapper?
The Data Mapper is a layer of software that separates the
in-memory objects from the database.
Its responsibility is to transfer data between the two and
also to isolate them from each other.
With Data Mapper the in-memory objects needn't know even
that there's a database present.
Martin Fowler
12. Doctrine
- 28.5 Milions of downloads
- Extended community
- 538 Contributors
- Inspired from Hibernate
Top italian contributors
#2 - Ocramius - 837 commits
(Marco Pivetta)
#12 - Jean85 - 28 commits
(Alessandro Lai)
13. Eloquent vs Doctrine
// Eloquent
$scientist = new Scientist();
$scientist->name = 'Albert';
$scientist->surname = 'Einstein';
$scientist->save();
$scientist->theories()->save(
Theory::create(['theory'=>'Theory of relativity'])
);
// Doctrine
$scientist = new Scientist(
'Albert',
'Einstein'
);
$scientist->addTheory(
new Theory('Theory of relativity')
);
EntityManager::persist($scientist);
EntityManager::flush();
14. Laravel Doctrine
253K ⇩ 425 ☆
Same concepts of Doctrine
- Entity
- EntityManager
- EntityRepository
15. Laravel + Doctrine: Set up
// Laravel 5.5
composer require "laravel-doctrine/orm:1.4.*"
Because of the auto package discovery feature Laravel 5.5
has, the ServiceProvider and Facades are automatically
registered.
php artisan vendor:publish --tag="config"
The DB configuration are inside the .env file, as usual
16. Cool and easy!
But how it works?
Our domain is a library
Let’s try with an example:
18. Entities - Metadata
- By @Annotation
- By <XML> mapping file </XML>
- By YML
mapping
file
19. Entities - Model
namespace AppEntities;
use DoctrineORMMapping as ORM;
/**
* Class Book
*
* @package AppEntities
*
* @ORMEntity()
* @ORMTable("books")
*/
class Book
22. Entities - Data accessing
public function __construct(string $isbn, string $title, Author $author)
{
$this->setIsbn($isbn); // Valid ISBN format
$this->setTitle($title); // Not empty
$this->author = $author; // Already validated
}
public function getIsbn(): string { … }
public function getTitle(): string { … }
public function getAuthor(): Author { … }
private function setIsbn(string $isbn): void { … }
private function setTitle(string $title) : void { … }
25. $repository = EntityManager::getRepository(Book::class);
Doctrine Entity Repository: Generic
interface ObjectRepository
{
public function find($id): object;
public function findAll(): array;
public function findBy(array $criteria): array;
public function findOneBy(array $criteria): array;
public function getClassName(): string;
}
26. Doctrine Entity Repository
interface BookRepository
{
public function findByIsbn(string $isbn): Book;
public function findByTitle(string $title): array;
public function findByAuthor(Author $author): array;
}
27. Doctrine Entity Repository: Inheritance
use DoctrineORMEntityRepository;
class DoctrineBookRepository extends EntityRepository implements BookRepository
{
public function findByAuthor(Author $author): array
{
return $this->findBy(['author' => $author]);
}
/* … */
}
28. Doctrine Entity Repository: Composition
class DoctrineBookRepository implements BookRepository
{
private $repository;
public function __construct(ObjectRepository $repository)
{
$this->repository = $repository;
}
public function findByAuthor(Author $author): array
{
return $this->repository->findBy(['author' => $author]);
}
/* … */
}
29. Icing on the cake
- Authentication:
Integrated with Laravel Auth
- Extensions:
Gedmo & Berbelei. Provides extra power to your entities
- Migrations:
Create migrations from models metadatas
- ACL:
Access Control out of the box
- Fluent:
Define the models metadatas using a fluent sintax
- Scout:
Add full-text search into your Doctrine entities