Simple REST API in Laminas Mezzio for managing book loans.
Portuguese version of this README.md here
Technical test proposed by Live eCommerce, being one of the first stages to be performed.
In this stage, the project needs to meet certain specifications, and I did my best to address them. Here is the link to the repository with the specifications.
The project consists of a book management system, specifically for book loans, allowing people to borrow and return books.
In this README document, I will detail some points I found interesting to comment on, in addition to documenting all the work I have done, thus serving as a repository for reference and personal portfolio.
To test the project, some prerequisites are necessary.
- Clone the repository
git clone https://github.com/Arekushi/library-management-api.git
- Run this command in the terminal
docker-compose up -d --build
Wait until the migrations are completed and Apache is initialized.
- Everything is set, the application is up and running ๐
- Run this command in the terminal to perform UNIT TESTS
composer run test
You can see all the routes by going to the endpoint: http://127.0.0.1:8080/api
This is the database schema developed, consisting of only 4 entities: book
, loan
, person
, and telephone
.
Since no ER (entity-relationship) diagram was provided, I created one that made sense and met the requirements.
The telephone
entity did not necessarily need to exist, but I used it to test the use of the mapper, to see how to use it when there is a collection of objects in one entity.
The book
entity is also quite simple, with nothing special.
The loan
entity could have been more complex, potentially managing the loan of multiple books at once, but this way it works for the purpose of the exercise.
Here are some directories and files that may help in the analysis.
- src/Abstract/
- src/Aspect/
- src/Person/src/
- Model/
- src/Library/src
- Model/
- Handler/
- Service/
- Repository/
When faced with the challenge in PHP, I knew I would need a robust framework to handle the requests. Initially, I thought of Laravel, which is quite popular, robust, and easy to use. However, since the challenge was in PHP and the job description specified that knowledge of Zend Framework/Laminas Mezzio would be interesting, I decided to venture into this framework for this project.
Yes, it was quite a challenge; I hadnโt programmed in PHP for years, so getting used to PHP programming took me a little while, as well as understanding some elements that compose Laminas Mezzio. But it was an incredible experience, and I feel I learned a lot from it. Itโs not my favorite framework by far, but it can meet the needs depending on the project.
I consider the documentation of Laminas Mezzio to be poor; it is not very explanatory for newcomers, and content on the internet is scarce. Itโs as if no one uses this framework, or if they do, they donโt share any material online. So Iโm happy to leave my repository here, open for anyone to consult and learn from it.
When considering an ORM to use with Laminas Mezzio, I initially thought of Doctrine, a famous and robust ORM. However, I encountered difficulties trying to integrate it with Laminas Mezzio. Iโm not sure, but apparently, it was designed for Laminas MVC. The only repository I found that had an integration of Doctrine with Laminas Mezzio is quite outdated and did not work as expected.
So, thinking about this, I asked ChatGPT if there were other ORMs, and of course, it suggested several, including LaminasDB and Cycle ORM.
The simplest choice would be to use LaminasDB, as it is already part of the Laminas ecosystem, making integration straightforward, which it was. However, in the first tests, I noticed that it is very basic; there arenโt many features to help and aid in development, which made me reconsider using it.
Cycle ORM does not have a page discussing its integration with Laminas Mezzio, but I tried to integrate it anyway, and I succeeded. Later, I found a repository that already had this integration in a more robust way, and I had to fork the project to fix some bugs and update the dependencies, and in the end, everything worked out.
Cycle ORM is packed with cool features; it seems quite robust and reminded me a bit of Prisma, which I have used in previous projects.
The only thing I felt didnโt work well was the migration schema; they didnโt work as they should, so I always had to delete the last migration and recreate it to make it work. I also identified a bug in a line of code in the project, and to fix it, I ended up creating a script to edit the line to correspond to the expected behavior.
The project has potential, but I feel itโs somewhat neglected, as it has been quite a while since there were any significant updates.
This is not something I do very often, and I confess that itโs a bit challenging to think of logic to create tests that make sense. I didnโt create the best tests; I recognize that. There is still a lot of room for improvement. Nevertheless, it was a learning experience and an additional practice in creating unit tests.
A programming paradigm that isnโt very popular, but one I have a certain appreciation for is Aspect-Oriented Programming. It is interesting due to the possibility of modularization and code reuse.
In this project, I created an aspect that validated the JSON body when present. This could be done within the method itself without the aid of an aspect, but from a development standpoint, I see it as beneficial to separate the business logic from certain actions that somewhat โpolluteโ the code. The goal is always to achieve a high level of code maintainability, and aspect-oriented programming can facilitate this.
Docker seems like a complex beast, but it is not quite so; it is a robust tool that provides greater ease in working, making the project scalable, portable, and ultimately deployable.
My current implementation has only two services: the API and the database. However, if I were to add a frontend, for instance, it would just be another built image inside this projectโs container.
As requested, I paid attention to the expected output at each endpoint and in every response given some context.
In this, the Symfony library helped quite a bit. In a very simple way, it is possible to add validations in a class, and thus, when a validation fails, it will throw an exception that can be caught and handled in the ExceptionHandler.
#[OAT\Schema(schema: 'CreatePersonRequest')]
class CreatePersonRequest
{
#[Assert\NotBlank(message: "The name cannot be empty.")]
#[Assert\Length(min: 3, minMessage: "The name must be at least 3 characters long.")]
#[OAT\Property(type: 'string')]
public string $name;
#[Assert\NotBlank(message: "The email cannot be empty.")]
#[Assert\Email(message: "The email '{{ value }}' is not a valid email address.")]
#[OAT\Property(type: 'string')]
public string $email;
}
Here is a simple example of the request object when creating a person. Each attribute allows you to add a validation, and if it fails, an exception is thrown.
As is customary, I always document my routes using Swagger, which is a robust tool that facilitates the documentation of routes and communication between developers.
To implement Swagger, it was necessary to create a fork of an implementation of Swagger with Laminas Mezzio and fix the dependencies and some bugs caused by the new versions.
All commits have an emoji as a prefix that indicates the type of change, along with a message that describes it.
These emojis were based on this site here, which presents several emojis and some meanings for their use.
Here is a link to a page on Notion, where you can find materials that supported me in the development of this application. ๐
Alexandre Ferreira de Lima alexandre.ferreira1445@gmail.com ๐ป |
---|