Open up a Terminal again, cd into learning folder and run:
composer require "laravel-doctrine/orm:1.1.*"
Likewise, install uuid-doctrine:
composer require ramsey/uuid-doctrine
Open config/app.php file and add into providers array the following line:
1
|
LaravelDoctrine\ORM\DoctrineServiceProvider:: class , |
In the same file, look for aliases array and add the following lines:
1
2
3
|
'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager:: class , 'Registry' => LaravelDoctrine\ORM\Facades\Registry:: class , 'Doctrine' => LaravelDoctrine\ORM\Facades\Doctrine:: class , |
Once you have added it, open up a terminal and run the following command to publish configuration above:
php artisan vendor:publish --tag="config"
Doctrine configuration
The config/doctrine.php file must be to modified as follows:
We edit the line:
1
|
'meta' => env( 'DOCTRINE_METADATA' , 'annotations' ), |
For:
1
|
'meta' => env( 'DOCTRINE_METADATA' , 'config' ), |
In this line, we are changing the way to map the entities, of annotations to configuration files (congif). There are several ways to map our entities with the tables in the database (annotations, yaml, xml, configuration files, static function in the entities), we chose configuration files because in this way, the definition of metadatas is separated of the entities and it adapts better configuration files used by Laravel.
Then, place the ‘mapping_file’ item below the ‘connection’ item, as follows:
1
2
3
|
'connection' => env( 'DB_CONNECTION' , 'mysql' ), 'mapping_file' => 'mappings' , 'namespaces' => [ |
With this, we are saying that the object-relational mapping config file will be called: mappings.php.
Then, to prevent that Doctrine searches the entities throughout the app folder, set the namespace where it will search, in this case App\Domain:
We change:
1
2
3
|
'namespaces' => [ 'App' ], |
For:
1
2
3
|
'namespaces' => [ 'App\Domain' ], |
Finally, in the same file, add the data type uuid for Doctrine
Change:
1
2
3
|
'custom_types' => [ 'json' => LaravelDoctrine\ORM\Types\Json:: class , ], |
To:
1
2
3
4
|
'custom_types' => [ 'json' => LaravelDoctrine\ORM\Types\Json:: class , 'uuid' => Ramsey\Uuid\Doctrine\UuidType:: class ], |
Working with Domain
Inside app/ directory, create a new folder called Domain and into this, create another folder calledTeacher, in this folder, create the Teacher.php file which will contain the Teacher entity, as shown in the picture:
The Teacher entity will have the attributes: $id and $name, in addition, it will contain the setters and getters methods, additionally it will contain a whitelist() method which will return an array of attributes which can be assigned, as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<?php namespace App\Domain\Teacher; class Teacher { protected $id ; protected $name ; public function getId() { return $this ->id; } public function getName() { return $this ->name; } public function setName( $name ) { $this ->name = $name ; } public function whitelist() { return [ 'name' ]; } } |
Mapping the entity with the table
Now, we must map the entity, for that, create the config/mappings.php and put the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<?php use Ramsey\Uuid\Doctrine\UuidGenerator; return [ 'App\Domain\Teacher\Teacher' => [ 'type' => 'entity' , 'table' => 'teachers' , 'id' => [ 'id' => [ 'type' => 'uuid' , 'generator' => [ 'strategy' => 'custom' ], 'customIdGenerator' => [ 'class' => UuidGenerator:: class ], ], ], 'fields' => [ 'name' => [ 'type' => 'string' ] ] ] ]; |
With this, Doctrine will know that the Teacher class will be recorded in the teachers table, and this will have an identifier field called id which will be generate through the UuidGenerator class (we also can generate this id if we create a constructor in the entity and inside it we put $this->id = Ramsey\Uuid\Uuid::uuid4() ) and the name field of type string.
Creating the database
On the mysql command line, create a empty database called learning:
create database learning;
We can do it using phpmyadmin or another mysql client.
Database Configuration
Edit the following fields into the Laravel config file .env according your local database settings:
1
2
3
4
|
DB_HOST=127.0.0.1 DB_DATABASE=learning DB_USERNAME=root DB_PASSWORD=123456 |
Run the following command to create the teachers table in the database through the mapping scheme:
php artisan doctrine:schema:create
Repository Interface
Now, let’s create the TeacherRepository.php file in app/Domain/Teacher as shown in the picture:
In this file we will create the TeacherRepository interface, which will have the methods: create,update, save, delete, find y findAll, those methods will be implemented after using Doctrine.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?php namespace App\Domain\Teacher; interface TeacherRepository { public function create( $data ); public function update( $data , $id ); public function save( $object ); public function delete ( $object ); public function find( $id ); public function findAll(); } |
Testing
Let’s create a test class using the Laravel artisan command:
php artisan make:test TeacherRepositoryTest
This command will create us the TeacherRepositoryTest.php in /test folder, in this file we will test methods of the repository, as shown in the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
<?php use App\Domain\Teacher\TeacherRepository; use App\Domain\Teacher\Teacher; class TeacherRepositoryTest extends TestCase { protected $repository ; protected static $teacher ; public function setUp() { parent::setUp(); $this ->repository = App::make(TeacherRepository:: class ); } public function testCreateAndSave() { $data = array ( 'name' => 'foo' ); $teacher = $this ->repository->create( $data ); self:: $teacher = $this ->repository->save( $teacher ); $this ->seeInDatabase( 'teachers' ,[ 'id' =>self:: $teacher ->getId()]); } public function testUpdateAndSave() { $data = array ( 'name' => 'bar' ); $teacher = $this ->repository->update( $data , self:: $teacher ->getId()); self:: $teacher = $this ->repository->save( $teacher ); $this ->assertEquals( $data [ 'name' ],self:: $teacher ->getName()); } public function testFindAll() { $teachers = $this ->repository->findAll(); $this ->assertContainsOnlyInstancesOf(Teacher:: class , $teachers ); } public function testDelete() { $teacher = $this ->repository->find(self:: $teacher ->getId()); $result = $this ->repository-> delete ( $teacher ); $this ->assertTrue( $result ); } } |
Doctrine Implementation
Create Infrastructure folder into app/ folder and into this folder create DoctrineBaseRepository.php file, as shown below:
In this file let’s create the DoctrineBaseRepository class, which extends of EntityRepository class of Doctrine. DoctrineBaseRepository will have the common and necessary methods which all the next repositories will have.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
<?php namespace App\Infrastructure; use Doctrine\ORM\EntityRepository; use Doctrine\Common\Util\Inflector; class DoctrineBaseRepository extends EntityRepository { public function create( $data ) { $entity = new $this ->_entityName(); return $this ->prepare( $entity , $data ); } public function update( $data , $id ) { $entity = $this ->find( $id ); return $this ->prepare( $entity , $data ); } public function prepare( $entity , $data ) { $set = 'set' ; $whitelist = $entity ->whitelist(); foreach ( $whitelist as $field ){ if (isset( $data [ $field ])){ $setter = $set .Inflector::classify( $field ); $entity -> $setter ( $data [ $field ]); } } return $entity ; } public function save( $object ) { $this ->_em->persist( $object ); $this ->_em-> flush ( $object ); return $object ; } public function delete ( $object ) { $this ->_em->remove( $object ); $this ->_em-> flush ( $object ); return true; } } |
The methods create() and update() create and update respectively the object by assigning its attributes through the method prepare() and the methods save() and delete() as their names imply, persist and remove those objects in the database.
Then, into the app/Infrastructure let’s create the Teacher folder and inside it let’s create the DoctrineTeacherRepository.php file, as shown in the following image:
In this file will be the DoctrineTeacherRepository class which will be the repository of persistence and recovery of data from the Teacher entity, this will extend the previouslyDoctrineBaseRepository class created to inherit all the basic and necessary methods and it will implement the TeacherRepository interface. Here you can add any other particular method that business logic required, for now we won’t need any other and we will leave it without additional methods:
1
2
3
4
5
6
7
8
9
|
<?php namespace App\Infrastructure\Teacher; use App\Domain\Teacher\TeacherRepository; use App\Infrastructure\DoctrineBaseRepository; class DoctrineTeacherRepository extends DoctrineBaseRepository implements TeacherRepository { } |
Let’s open the app/Providers/AppServiceProvider.php file and into the register() method add the code which will instance DoctrineTeacherRepository class when the TeacherRepository interface is used.
1
2
3
4
5
6
7
8
9
10
|
public function register() { $this ->app->bind(\App\Domain\Teacher\TeacherRepository:: class , function ( $app ) { // This is what Doctrine's EntityRepository needs in its constructor. return new \App\Infrastructure\Teacher\DoctrineTeacherRepository( $app [ 'em' ], $app [ 'em' ]->getClassMetaData(\App\Domain\Teacher\Teacher:: class ) ); }); } |
Testing the repository
With this we can use the repository created.
To test if it works, let’s use the TeacherRepositoryTest class made earlier, running the command:
vendor/bin/phpunit
We should get the following answer:
This tells us that through the repository we were able to create, update, view and delete a record in the database.