How to Insert a validation before DB INSERT (SLIM4 API SKELETON)

I’m working with SLIM4 API SKELETON by Mauro Bonfietti.

From my little experience, I was able to run the app and include CRUDs for my tables. Now to mold the app to my needs.

There are the Sevice and Repository phps for each table. I would like to add a CONDITION before a create/insert is done.

As an example, I have a visits table. It has visitor_id and datetime of visit. every time this visitor visits, the app inserts the visitor_id and the datetime of visit. As for the condition, let us say, the app only insert a record if the visitor’s last visit is more than 10mins older than the current visit.

You just need to fetch your visits table by your visitor_id before, and in case if the result is not empty compare datetime registered with the current one. Afterwards, depending on the result you process insert/update or not.

Thanks for the reply, what I’m looking for though is ‘where/how’ to code.

Do I create a function that checks for the condition within the visitsservice or visitsrepository. After checking with the condition, how do I code if the condition checks and at the same time if it doesn’t satisfy the condition.

Also, there is a directory for visits inside Controller directory, too.

Lets start with the basics - basic CRUD flow code structure:

  1. First option:
  • 4 routes - CREATE visit, READ visit, UPDATE visit, DELETE visit
  • 1 controller - VisitController
    • with VisitService injected into contstructor
    • with 4 actions(methods) create, get, update, delete calling related VisitService methods
  • 1 service - VisitService
    • with injected VisitRepository
    • with 4 methods create, get, update, delete
  1. Second option:
  • 4 routes - CREATE visit, READ visit, UPDATE visit, DELETE visit
  • 4 action controllers - CreateVisitActionController, etc.
    • each one with VisitService injected into contstructor
    • each one with single __invoke method calling related VisitService method
  • 1 service - VisitService
    • with injected VisitRepository
    • with 4 methods create, get, update, delete
  1. Third option:
  • 4 routes - CREATE visit, READ visit, UPDATE visit, DELETE visit
  • 4 action controllers - CreateVisitActionController, etc.
    • each one with its proper ActionService injected into constructor or as a parameter of __invoke method
    • each one with single __invoke method calling related VisitActionService by its __ivoke method
  • 4 action services - CreateVisitActionService, etc.
    • each one with injected VisitRepository
    • each one with with single __invoke method
  1. My favorite option:
  • 4 routes - CREATE visit, READ visit, UPDATE visit, DELETE visit
  • 1 controller - VisitController
    • with 4 actions(methods) create, get, update, delete
    • with 4 VisitActionServices injected as parameter into proper controller’s action(method) - CreateVisitActionService, etc…
  • 4 action services - CreateVisitActionService, etc.
    • each one with injected VisitRepository into constructor
    • each one with with single __invoke method

No matter which option you select, your issue is related to create/add process, so alternatively:

  • in your VisitService create() method or
  • in your CreateVisitActionService __invoke method you are supposed to call your VisitRepository two times depending on the situation:
    • $this->visitRepository->getById($id)
    • check required condition
    • and (or not) $this->visitRepository->create($visit)
1 Like

Just a follow, is this true if I also want to add additional information to the result.

For example, I GET an array filled with visit count by category/type. Then I want to add percentage per category/type of visit.

Regardless of an approach, it is important to leave CRUD’s actionService or service’s methods (I mean those which are able to query database) as generic as possible.
You can query count but correct me if I am wrong with percent it is not that obvious.
If you need any additional operations (transformations, calculations) do it by an additional service having appropriate method that accepts generic results of the first one as a parameter and returns them as you need:

Controller’s action example without additional transformations:

    public function getCategoriesCount(
        Request $request,
        Response $response,
        GetCategoriesCountAction $action
    ): Response {
        return ($this->responder)(
            $response,
            $action($request)
        );
    }

Controller’s action example with additional transformations:

    public function getCategoriesPercentage(
        Request $request,
        Response $response,
        GetCategoriesCountAction $action,
        TransformationService $service
    ): Response {
        $result = $action($request);
        return ($this->responder)(
            $response,
            $service->transformCountIntoPercentage($result)
        );
    }

Hello,

I’m not sure how to code this. Here is my file organization:

Contoller folder
--Visits folder
  --Base.php
  --GetCount.php
      final class GetCount extends Base {
        public function __invoke(Request $request, Response $response): Response
          getVisitsService()->getCount($input);
      }

Repository folder
--VisitsRepository.php
  final class VisitsRepository {
    public function getCount(object $visits): array {
      // database query here.
    }
  }

Service folder
--VisitsService.php
  final class VisitsService {
    public function getCount(array $input): array {
      return $this->visitsRepository->getCount($visits);
    }
  }

Forgive me if I’m very slow at this. I believe my CRUD follows Second option. My question is: where do I place the functions getCategoriesCount and getCategoriesPercentage?

My question is: where do I place the functions getCategoriesCount and getCategoriesPercentage ?

It depends.

You do not present any details about your application schema, so for a simple case scenario we are dealing with two separate scopes - Visit and Category. So everything related to Visit stays within Visits folder, and what is related to Category stays within Categories folder.
So, your count and percentage appears to be 5 th and 6 th after 4 actions related to your basic CRUD. However …
If you intend to count or get percentage of Category being in relation with some Visit you have to consider a necessity of a third scope VisitCategory, within a separate folder and with separate set of classes (controller, actions, repositories, etc …) responsible for all requests related to a relation VisitCategory as a whole.

1 Like

Does the application schema means my database and the tables and fields I’m working on? Currently it’s like this:

  1. Visits table (id, visitor_id, created_at, updated_at)
  2. Visitors table (id, visitor_id, name, grade, section, teacher, type, created_at, updated_at)

Looking into this, I think, I may need to modify my visitors table a little more to accommodate some more situations. Like: school year.

  1. Batch table (id, visitor_id, school_year, grade, section, teacher, created_at, updated_at)
  2. new Visitors table (id, visitor_id, name, type, created_at, updated_at)

The count and percentage per category is say per grade. So I’m going to name the scope VisitsGrade and then create the necessary folders and classes. Is this okay? Also for this, I’m just going to do queries that involve more than 1 table.

Visits table - What kind of visits? many-to-many meaning sort of museum trips related to a group of visitors (students,workers,tourists) or one-to-one meaning some single person consultations with a doctor?

Visitors table - I don’t think this table is necessary. Regardless of kind of a visitor (student, worker, teacher, tourist) your visitor_id may be easily related to any external id in Students or Workers or Teachers etc tables and it is wrong to repeat already existing information.

NewVisitors table - what do you need it for?

For one-to-one I’d rather create:

Visits table (id, ref, visitor_id, created_at, updated_at)
Students table (id, name, grade_id, section_id, teacher_id, created_at, updated_at)
Teachers table (id, name, created_at, updated_at)
Grade table (id, ref)
Section table (id, ref)

SELECT *
FROM visits 
LEFT JOIN students ON visits.visitor_id = students.id
    LEFT JOIN grades ON students.grade_id = grades.id
    LEFT JOIN sections ON students.section_id = sections.id
    LEFT JOIN teachers ON students.teacher_id = tearchers.id
WHERE students.grade_id = 'A'

where grades and sections are simple tables representing list of references.

This way you have 5 scopes represented by folders Visits, Students, Grades, Sections, Teachers.
There is no need to create an additional VisitsGrades scope, because your field grade is already within the scope of Visit by referenced student_id.

For the case of many-to-many situation changes:

Now you need the third scope, VisitsStudents:

  • VisitsStudents table (id, visit_id, student_id)

with slight modification of joined tables:

  • Visits table (id, ref, created_at, updated_at)
  • Students table (id, name, grade_id, section_id, teacher_id, created_at, updated_at)
SELECT *
FROM visits_students 
LEFT JOIN visits ON visits_students.visit_id = visits.id
LEFT JOIN students ON visits_students.student_id = students.id
    LEFT JOIN grades ON students.grade_id = grades.id
    LEFT JOIN sections ON students.section_id = sections.id
    LEFT JOIN teachers ON students.teacher_id = teachers.id
WHERE students.grade_id = 'A'  

As you can see there is lots of JOIN s in sql queries, so this is a case where it is wise to use prefixes to avoid table fields names conflicts, for example: idstd_id.

Yes, meaning how often a student/teacher visit the museum or the library and such.

It’s the newly modified version of Visitors table.

Yes, I believe I’ll be creating the db this way. I would also like to point out the Batch table (for me it means managing the school year. Students will attend the same school every year and will have different grade level.

Always appreciated this discussion. Thanks, @tj_gumis .