Introduce
Hello guys, it’s been a while since I wrote a post. Today I share about the topic of separating the repo and service layers in Cakephp 4 . The content is just me learning and sharing to learn and save. If you have any questions or suggestions for me, please comment to share, learn and develop.
1. What is Repository?
In software programming, a Repository is a design pattern used to abstract and separate data storage from objects in the system. The Repository pattern provides a generic and abstract approach to data access, making it easy to change the way data is stored and retrieved without changing the objects that use that data.
In applications that use databases (databases), the Repository pattern is often used to retrieve and store data, and to interact with tables in the database. Repository methods usually include basic operations such as create, read, edit, delete (CRUD – Create, Read, Update, Delete), and other data query methods.
The Repository pattern is part of the SOLID design principles, which increases modularity, reduces dependencies between system components, and eases future maintenance and changes to data storage.
2. What is Service?
In programming, Service is a component of software architecture (architecture) used to handle the application’s business tasks. In other words, a Service is a class that handles certain tasks related to the application’s business. Service can call objects and methods from other classes such as Repository, Model, … and return results to Controller, View or other Service classes.
Business tasks may include data processing, data checking and validation, emailing, report generation, database queries, calculations, integration of other services,… Service helps separate business tasks from other layers in the MVC architecture, simplifying application development and maintenance.
Okay, I’ll go into the details now.
Main content
In Cakephp 4, there is support for separating the Repository and Service sections. You can use the repository tier to perform queries to the database and use the service tier to write data processing logic. Here is an example of how to use abstract class and interface to create base repository and service in CakePHP 4:
1. Create Base for Repository
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">namespace</span> <span class="token package">App Repositories Interfaces</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake ORM Query</span> <span class="token punctuation">;</span> <span class="token keyword">interface</span> <span class="token class-name-definition class-name">IBaseRepository</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">find</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token class-name return-type">Query</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">save</span> <span class="token punctuation">(</span> <span class="token variable">$entity</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword return-type">bool</span> <span class="token punctuation">;</span> <span class="token comment">// ....</span> <span class="token punctuation">}</span> |
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 | <span class="token keyword">namespace</span> <span class="token package">App Repositories Eloquents</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake ORM Query</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake ORM RulesChecker</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake ORM Table</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake Validation Validator</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Repositories Interfaces IBaseRepository</span> <span class="token punctuation">;</span> <span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name-definition class-name">BaseRepository</span> <span class="token keyword">implements</span> <span class="token class-name">IBaseRepository</span> <span class="token punctuation">{</span> <span class="token keyword">protected</span> <span class="token variable">$table</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span> <span class="token punctuation">(</span> <span class="token class-name type-declaration">Table</span> <span class="token variable">$table</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">table</span> <span class="token operator">=</span> <span class="token variable">$table</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">find</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token class-name return-type">Query</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">table</span> <span class="token operator">-></span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">save</span> <span class="token punctuation">(</span> <span class="token variable">$entity</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword return-type">bool</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token keyword type-casting">bool</span> <span class="token punctuation">)</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">table</span> <span class="token operator">-></span> <span class="token function">save</span> <span class="token punctuation">(</span> <span class="token variable">$entity</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
2. Create Example Repo
Below, when using the service, please create a new Repo, I will create an ArticleRepository for example:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">namespace</span> <span class="token package">App Repositories Interfaces</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake ORM Query</span> <span class="token punctuation">;</span> <span class="token keyword">interface</span> <span class="token class-name-definition class-name">IArticleRepository</span> <span class="token keyword">extends</span> <span class="token class-name">IBaseRepository</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">find</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token class-name return-type">Query</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">save</span> <span class="token punctuation">(</span> <span class="token variable">$entity</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword return-type">bool</span> <span class="token punctuation">;</span> <span class="token comment">// ....</span> <span class="token punctuation">}</span> |
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 | <span class="token keyword">namespace</span> <span class="token package">App Repositories Eloquents</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake ORM Query</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake ORM RulesChecker</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake ORM Table</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake Validation Validator</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">App Repositories Interfaces IArticleRepository</span> <span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name-definition class-name">ArticleRepository</span> <span class="token keyword">extends</span> <span class="token class-name">BaseRepository</span> <span class="token keyword">implements</span> <span class="token class-name">IArticleRepository</span> <span class="token punctuation">{</span> <span class="token keyword">protected</span> <span class="token variable">$table</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span> <span class="token punctuation">(</span> <span class="token class-name type-declaration">Table</span> <span class="token variable">$table</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">table</span> <span class="token operator">=</span> <span class="token variable">$table</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">find</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token class-name return-type">Query</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">table</span> <span class="token operator">-></span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">save</span> <span class="token punctuation">(</span> <span class="token variable">$entity</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword return-type">bool</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token keyword type-casting">bool</span> <span class="token punctuation">)</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">table</span> <span class="token operator">-></span> <span class="token function">save</span> <span class="token punctuation">(</span> <span class="token variable">$entity</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Note that you need to register the repository and service with the DI container before using them. You can register using the DI container’s set() method. For example, if you want to register an instance of ArticleRepository .
In the src/Application.php file, you can create properties to store the configurations of the repositories. For example:
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 | <span class="token keyword">protected</span> <span class="token variable">$repositories</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string single-quoted-string">'ArticleRepository'</span> <span class="token punctuation">,</span> <span class="token string single-quoted-string">'UserRepository'</span> <span class="token punctuation">,</span> <span class="token string single-quoted-string">'CommentRepository'</span> <span class="token punctuation">,</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">bootstrap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword return-type">void</span> <span class="token punctuation">{</span> <span class="token comment">// Register repositories</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token function">registerRepositories</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">function</span> <span class="token function-definition function">registerRepositories</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword return-type">void</span> <span class="token punctuation">{</span> <span class="token keyword">foreach</span> <span class="token punctuation">(</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">repositories</span> <span class="token keyword">as</span> <span class="token variable">$repository</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$interface</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'AppRepositoriesInterfacesI'</span> <span class="token operator">.</span> <span class="token variable">$repository</span> <span class="token punctuation">;</span> <span class="token variable">$class</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'AppRepositoriesEloquents\'</span> <span class="token operator">.</span> <span class="token variable">$repository</span> <span class="token punctuation">;</span> <span class="token comment">// Register repository implementation with DI container</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token function">getContainer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token function">set</span> <span class="token punctuation">(</span> <span class="token variable">$class</span> <span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token variable">$container</span> <span class="token punctuation">)</span> <span class="token keyword">use</span> <span class="token punctuation">(</span> <span class="token variable">$class</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token variable">$class</span> <span class="token punctuation">(</span> <span class="token comment">/* pass in any necessary dependencies */</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Register interface with DI container</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token function">getContainer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token function">set</span> <span class="token punctuation">(</span> <span class="token variable">$interface</span> <span class="token punctuation">,</span> <span class="token variable">$class</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
3. Create Service
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 | <span class="token keyword">namespace</span> <span class="token package">App Services</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">App Repositories Interfaces IArticleRepository</span> <span class="token keyword">as</span> ArticleRepository <span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name-definition class-name">ArticleService</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token variable">$articleRepository</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span> <span class="token punctuation">(</span> <span class="token class-name type-declaration">ArticleRepository</span> <span class="token variable">$articleRepository</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">articleRepository</span> <span class="token operator">=</span> <span class="token variable">$articleRepository</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getAll</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">articleRepository</span> <span class="token operator">-></span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token function">all</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">save</span> <span class="token punctuation">(</span> <span class="token variable">$data</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Validate data here</span> <span class="token variable">$article</span> <span class="token operator">=</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">articleRepository</span> <span class="token operator">-></span> <span class="token function">newEntity</span> <span class="token punctuation">(</span> <span class="token variable">$data</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">articleRepository</span> <span class="token operator">-></span> <span class="token function">save</span> <span class="token punctuation">(</span> <span class="token variable">$article</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token variable">$article</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//... </span> <span class="token punctuation">}</span> |
We will also be signing the service in src/Application.php
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 | <span class="token keyword">protected</span> <span class="token variable">$services</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string single-quoted-string">'ArticleService'</span> <span class="token punctuation">,</span> <span class="token string single-quoted-string">'UserService'</span> <span class="token punctuation">,</span> <span class="token string single-quoted-string">'CommentService'</span> <span class="token punctuation">,</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">bootstrap</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword return-type">void</span> <span class="token punctuation">{</span> <span class="token comment">// Register repositories - này là ở step trên khi đang kí repo</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token function">registerRepositories</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Register services</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token function">registerServices</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">protected</span> <span class="token keyword">function</span> <span class="token function-definition function">registerServices</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword return-type">void</span> <span class="token punctuation">{</span> <span class="token keyword">foreach</span> <span class="token punctuation">(</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">services</span> <span class="token keyword">as</span> <span class="token variable">$service</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//$interface = 'App\Service\' . $service . 'Interface'; // theo chuẩn nguyên lý SOLID thì service cũng sẽ phải tạo ra Interface , </span> <span class="token variable">$class</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'App\Service\'</span> <span class="token operator">.</span> <span class="token variable">$service</span> <span class="token punctuation">;</span> <span class="token comment">// Register service implementation with DI container</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token function">getContainer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token function">set</span> <span class="token punctuation">(</span> <span class="token variable">$class</span> <span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token variable">$container</span> <span class="token punctuation">)</span> <span class="token keyword">use</span> <span class="token punctuation">(</span> <span class="token variable">$class</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token variable">$class</span> <span class="token punctuation">(</span> <span class="token comment">/* pass in any necessary dependencies */</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Register interface with DI container</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token function">getContainer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token function">set</span> <span class="token punctuation">(</span> <span class="token variable">$interface</span> <span class="token punctuation">,</span> <span class="token variable">$class</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
4. DI Service into Controller
- Declare the $serviceName variable in the controller’s __construct method, for example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">namespace</span> <span class="token package">App Controller</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">App Service ArticleService</span> <span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">Cake Http Controller</span> <span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name-definition class-name">ArticlesController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token variable">$articleService</span> <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span> <span class="token punctuation">(</span> <span class="token class-name type-declaration">ArticleService</span> <span class="token variable">$articleService</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">articleService</span> <span class="token operator">=</span> <span class="token variable">$articleService</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span> |
- Use the $articleService variable in your controller methods, for example:
1 2 3 4 5 6 7 8 9 | <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">index</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$articles</span> <span class="token operator">=</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token property">articleService</span> <span class="token operator">-></span> <span class="token function">getAllArticles</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token variable">$this</span> <span class="token operator">-></span> <span class="token function">set</span> <span class="token punctuation">(</span> <span class="token function">compact</span> <span class="token punctuation">(</span> <span class="token string single-quoted-string">'articles'</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
In the index method, we use the $articleService variable to call the service’s getAllArticles() method. Note that, when using Dependency Injection in CakePHP 4, you don’t need to create the instance of the Service using new but instead, CakePHP will automatically create the instance and inject it into the Controller as needed. In the above example, when creating the ArticlesController instance, CakePHP will automatically create the ArticleService instance and pass in the __construct method.
End
Hope the above sharing will help you. Thanks for watching.