In AtroCore, services are the core components for housing business logic. They are standard PHP classes that contain the application's functionality. While controllers and other classes may handle request-response cycles, their primary purpose is to be minimal and delegate complex operations to service methods. This approach ensures a clean separation of concerns.


Default Entity Behavior

When a new entity is created, AtroCore automatically handle a default controller and service, providing a set of ready-to-use API endpoints without requiring any custom code.

  • Default Controller: An instance of \Atro\Core\Templates\Controllers\{type}.
  • Default Service: An instance of \Atro\Core\Templates\Services\{type}.

Here, {type} corresponds to the entity type: Base, Hierarchy, ReferenceData, or Relation.

These default components enable a variety of common actions out-of-the-box, including:

  • read (Get an entity)
  • create (Create an entity)
  • edit, update (Edit or update an entity)
  • MassUpdate (Mass update entities)
  • MassDelete (Mass delete entities)
  • Get related (Get related entities)
  • create relate (Link a related entity)
  • delete relate (Unlink a related entity)
  • mass Relate, mass unRelate (Batch-link or unlink multiple related entities)
  • subscribe, or unsubscribe (Subscribe or unsubscribe to entity activity updates)

The API documentation for these default actions is automatically available. Note that some actions may be deactivated for specific entities based on business requirements.


Customizing Controllers and Services

To add custom business logic or new API routes, you must create your own controller and service classes. This involves creating a new PHP file for each, following the standard directory structure and namespace conventions.

  • Controller Path: Controllers/{EntityName}.php
  • Controller Namespace: \ModuleName\Controllers\{EntityName}
  • Service Path: Services/{ServiceName}.php
  • Service Namespace: \ModuleName\Services\{ServiceName}

Example: Adding a Statistics Route to the Manufacturer Entity

Let's use the Manufacturer entity from the Entities section to demonstrate how to create a custom route that provides statistics on active manufacturers and those without products.

1. Create the Custom Controller

Create the file Controllers/Manufacturer.php and add the following code. The actionStatistic method is the entry point for our new route, which will be accessible at /Manufacturer/action/statistic. It validates the request and delegates the core logic to the service.

<?php

namespace ExampleModule\Controllers;

use Atro\Core\Exceptions\BadRequest;
use Atro\Core\Templates\Controllers\Base;

class Manufacturer extends Base
{
    public function actionStatistic($params, $data, $request): array
    {
        if (!$request->isGet()) {
            throw new BadRequest();
        }

        // Use $request->get('requestParam') to access URL parameters.
        // Use $data for POST parameters (not applicable for this GET request).
        return $this->getRecordService()->getStatics();
    }
}

2. Create the Custom Service

Next, create the file Services/Manufacturer.php. This service class contains the business logic to query the database and retrieve the required statistics.

<?php

namespace ExampleModule\Services;

use Atro\Core\Exceptions\BadRequest;
use Atro\Core\Templates\Service\Base;

class Manufacturer extends Base
{
    public function getStatics(): array
    {
        //Use the query builder instead of the build-in ORM to be more efficient
        $connection = $this->getEntityManager()->getConnection();

        // Query to count active manufacturers
        $activeManufacturers = $connection->createQueryBuilder()
            ->select('count(*)')
            ->from($connection->quoteIdentifier('manufacturer'))
            ->where('deleted = :false')
            ->andWhere('is_active = :true')
            ->setParameter('false', false, \Doctrine\DBAL\ParameterType::BOOLEAN)
            ->setParameter('true', true, \Doctrine\DBAL\ParameterType::BOOLEAN)
            ->fetchOne();

        // Query to count manufacturers without any linked products
        $manufacturerWithoutProducts = $connection->createQueryBuilder()
            ->select('count(*)')
            ->from($connection->quoteIdentifier('manufacturer'))
            ->where('deleted = :false')
            ->andWhere('id NOT IN (SELECT manufacturer_id from product WHERE manufacturer_id IS NOT NULL)')
            ->setParameter('false', false, \Doctrine\DBAL\ParameterType::BOOLEAN)
            ->fetchOne();

        return [
            "active" => $activeManufacturers,
            "withoutProducts" => $manufacturerWithoutProducts
        ];
    }
}

Documenting the New Route

AtroCore requires all custom routes to be documented to ensure the API is valid and functional. Documentation is handled in the Resources/routes.json file and adheres to the OpenAPI 3.0 specification.

Add the following JSON object to your routes.json file to document the new statistic route:

{
  "route": "/Manufacturer/action/statistic",
  "method": "get",
  "params": {
    "controller": "Manufacturer",
    "action": "statistic"
  },
  "description": "Get count of active manufacturers and manufacturers without products",
  "security": [
    {
      "basicAuth": []
    },
    {
      "Authorization-Token": []
    }
  ],
  "response": {
    "type": "object",
    "properties": {
      "active": {
        "type": "integer",
        "example": "0"
      },
      "withoutProducts": {
        "type": "integer",
        "example": "0"
      }
    }
  }
}

After adding this documentation, you can test the new endpoint with a GET request.

GET /api/v1/Manufacturer/action/statistic HTTP/1.1
Host: {https://INSTANCE_URL}
Authorization-Token: ****************