Skip to content

5.x advanced guide

Rajitha Kumara edited this page Jul 6, 2022 · 5 revisions

Dependency managers

  • Composer 2.x (Composer 1.x not supporting)
  • Yarn

Composer dependencies for backend

composer.json location src/composer.json

Add new composer dependency

cd <project root directory>
cd src

composer require symfony/http-foundation

# install specific version
composer require symfony/http-foundation:4.4.23

# dev dependency
composer require --dev symfony/maker-bundle

Yarn dependencies for frontend

package.json location src/client/package.json

Add new node module

cd <project root directory>
cd src/client

yarn add axios

# install specific version
yarn add axios@0.21.1

# dev dependency
yarn add --dev prettier

Backend related ...

Autoloading Classes

PSR-4 composer autoloader used for PHP class autoloading.

src/composer.json

  "autoload": {
    "psr-4": {
      "OrangeHRM\\ORM\\": "./lib/orm",
      "OrangeHRM\\Framework\\": "./lib/framework",
      "OrangeHRM\\Config\\": "./lib/config",
      "OrangeHRM\\Admin\\": "./plugins/orangehrmAdminPlugin",
      "OrangeHRM\\Authentication\\": "./plugins/orangehrmAuthenticationPlugin",
      "OrangeHRM\\Core\\": "./plugins/orangehrmCorePlugin",
      "OrangeHRM\\Pim\\": "./plugins/orangehrmPimPlugin",

      "OrangeHRM\\Entity\\": [
        "./plugins/orangehrmAdminPlugin/entity",
        "./plugins/orangehrmPerformancePlugin/entity",
        "./plugins/orangehrmPimPlugin/entity",
        "./plugins/orangehrmCorePlugin/entity"
      ]
    }
  },
  "autoload-dev": {
    "psr-4": {
      "OrangeHRM\\Tests\\": "./test/phpunit",
      "OrangeHRM\\Tests\\Admin\\": "./plugins/orangehrmAdminPlugin/test",

    }
  },

It's required dump autoload files after change autoload or autoload-dev in composer.json

cd <project root directory>
cd src

composer dump-autoload

Autoload classes runtime (without touching composer.json)

In each plugin in src/plugins directory contains configuration class, which initialize plugin configuration.

e.g. src/plugins/orangehrmAdminPlugin/config/AdminPluginConfiguration.php

Within that configuration file we can define PSR-4 autoloading

e.g.

// src/plugins/orangehrmAdminPlugin/config/AdminPluginConfiguration.php
<?php

use OrangeHRM\Framework\Http\Request;
use OrangeHRM\Framework\PluginConfigurationInterface;

class AdminPluginConfiguration implements PluginConfigurationInterface
{
    /**
     * @inheritDoc
     */
    public function initialize(Request $request): void
    {
        $loader = new \Composer\Autoload\ClassLoader();
        $loader->addPsr4('OrangeHRM\\Admin\\', [realpath(__DIR__ . "/..")]);
        $loader->addPsr4('OrangeHRM\\Entity\\', [realpath(__DIR__ . "/../entity")]);
        $loader->register();
    }
}

Note: Runtime autoloading will not support for phpunit unit testing

Front Controller

Application front controller can find web/index.php.

Read more about front controller: Design pattern, Symfony Documentation

Routing

All route files loading through OrangeHRM\Framework\RouteManager and not handled through caching yet. In the routing manager it's only support for symfony YAML resource type. Annotations, Attributes, XML, PHP files routing resource types are not supporting.

YAML routing files are defined under each plugin's config/routes.yaml.

e.g.

  • src/plugins/orangehrmAdminPlugin/config/routes.yaml
  • src/plugins/orangehrmPimPlugin/config/routes.yaml

API routing

# src/plugins/orangehrmPimPlugin/config/routes.yaml
apiv2_pim_employee: # resource endpoint
  path: /api/v2/pim/employees/{empNumber}
  controller: OrangeHRM\Core\Controller\Rest\V2\GenericRestController::handle
  methods: [ GET, PUT ]
  defaults:
    _api: OrangeHRM\Pim\Api\EmployeeAPI # OrangeHRM internal config key to specify API endpoint class 
    _key: empNumber # OrangeHRM internal config key to identify resource identifier, default: {id}

apiv2_pim_employees: # collection endpoint
  path: /api/v2/pim/employees
  controller: OrangeHRM\Core\Controller\Rest\V2\GenericRestController::handle
  methods: [ GET, POST, DELETE ]
  defaults:
    _api: OrangeHRM\Pim\Api\EmployeeAPI

Screen routing

# src/plugins/orangehrmAdminPlugin/config/routes.yaml
view_job_title: # List view
  path: /admin/viewJobTitleList
  controller: OrangeHRM\Admin\Controller\JobTitleController::handle
  methods: [ GET ]

save_job_title: # Save screen
  path: /admin/saveJobTitle
  controller: OrangeHRM\Admin\Controller\SaveJobTitleController::handle
  methods: [ GET ]

edit_job_title: # Edit screen
  path: /admin/saveJobTitle/{id}
  controller: OrangeHRM\Admin\Controller\SaveJobTitleController::handle
  methods: [ GET ]

API v2 Structure

Endpoint

<?php
namespace OrangeHRM\<Module>\Api;

use OrangeHRM\Core\Api\CommonParams;
use OrangeHRM\Core\Api\V2\CrudEndpoint;
use OrangeHRM\Core\Api\V2\Endpoint;
use OrangeHRM\Core\Api\V2\EndpointCollectionResult;
use OrangeHRM\Core\Api\V2\EndpointResourceResult;
use OrangeHRM\Core\Api\V2\EndpointResult;
use OrangeHRM\Core\Api\V2\Model\ArrayModel;
use OrangeHRM\Core\Api\V2\ParameterBag;
use OrangeHRM\Core\Api\V2\Validator\ParamRuleCollection;

class <Demo>API extends Endpoint implements CrudEndpoint
{
    /**
     * @inheritDoc
     */
    public function getOne(): EndpointResult
    {
        // ...
        return new EndpointResourceResult(ObjectModel::class, $object);
    }

    /**
     * @inheritDoc
     */
    public function getValidationRuleForGetOne(): ParamRuleCollection
    {
        return new ParamRuleCollection();
    }

    /**
     * @inheritDoc
     */
    public function getAll(): EndpointResult
    {
        // ...
        return new EndpointCollectionResult(
            ObjectModel::class, $objects,
            new ParameterBag([CommonParams::PARAMETER_TOTAL => $total])
        );
    }

    /**
     * @inheritDoc
     */
    public function getValidationRuleForGetAll(): ParamRuleCollection
    {
        return new ParamRuleCollection();
    }

    /**
     * @inheritDoc
     */
    public function create(): EndpointResult
    {
        // ...
        return new EndpointResourceResult(ObjectModel::class, $object);
    }

    /**
     * @inheritDoc
     */
    public function getValidationRuleForCreate(): ParamRuleCollection
    {
        return new ParamRuleCollection();
    }

    /**
     * @inheritDoc
     */
    public function update(): EndpointResult
    {
        // ...
        return new EndpointResourceResult(ObjectModel::class, $object);
    }

    /**
     * @inheritDoc
     */
    public function getValidationRuleForUpdate(): ParamRuleCollection
    {
        return new ParamRuleCollection();
    }

    /**
     * @inheritDoc
     */
    public function delete(): EndpointResult
    {
        // ...
        return new EndpointResourceResult(ArrayModel::class, $ids);
    }

    /**
     * @inheritDoc
     */
    public function getValidationRuleForDelete(): ParamRuleCollection
    {
        return new ParamRuleCollection();
    }
}

Endpoint Result

<?php
namespace OrangeHRM\<Module>\Api;

// ...
use OrangeHRM\Core\Api\V2\EndpointCollectionResult;
use OrangeHRM\Core\Api\V2\EndpointResourceResult;
use OrangeHRM\Core\Api\V2\EndpointResult;

class <Demo>API extends Endpoint implements CrudEndpoint
{
    /**
     * @inheritDoc
     */
    public function getOne(): EndpointResult
    {
        // ...
        return new EndpointResourceResult(ObjectModel::class, $object);
    }

    /**
     * @inheritDoc
     */
    public function getAll(): EndpointResult
    {
        // ...
        return new EndpointCollectionResult(
            ObjectModel::class, $objects,
            new ParameterBag([CommonParams::PARAMETER_TOTAL => $total])
        );
    }

    /**
     * @inheritDoc
     */
    public function create(): EndpointResult
    {
        // ...
        return new EndpointResourceResult(ObjectModel::class, $object);
    }

    /**
     * @inheritDoc
     */
    public function update(): EndpointResult
    {
        // ...
        return new EndpointResourceResult(ObjectModel::class, $object);
    }
}

Endpoint Result Models

<?php
namespace OrangeHRM\<Module>\Api\Model;

use OrangeHRM\Core\Api\V2\Serializer\ModelTrait;
use OrangeHRM\Core\Api\V2\Serializer\Normalizable;
use OrangeHRM\Entity\<Object>;

class ToDoModel implements Normalizable
{
    use ModelTrait;

    public function __construct(<Object> $object)
    {
        $this->setEntity($object);
        $this->setFilters(
            [
                'id',
                'name',
                ['getDecorator', 'getDate'],
            ]
        );
        $this->setAttributeNames(
            [
                'id',
                'name',
                'date',
            ]
        );
    }
}

Endpoint Validation Rules

<?php
namespace OrangeHRM\<Module>\Api;

//...
use OrangeHRM\Core\Api\V2\Validator\ParamRule;
use OrangeHRM\Core\Api\V2\Validator\ParamRuleCollection;
use OrangeHRM\Core\Api\V2\Validator\Rule;
use OrangeHRM\Core\Api\V2\Validator\Rules;

class <Demo>API extends Endpoint implements CrudEndpoint
{
    /**
     * @inheritDoc
     */
    public function getValidationRuleForGetOne(): ParamRuleCollection
    {
        return new ParamRuleCollection(
            new ParamRule(
                CommonParams::PARAMETER_ID,
                new Rule(Rules::POSITIVE)
            )
        );
    }

    /**
     * @inheritDoc
     */
    public function getValidationRuleForGetAll(): ParamRuleCollection
    {
        return new ParamRuleCollection(
            ...$this->getSortingAndPaginationParamsRules(<Object>SearchFilterParams::ALLOWED_SORT_FIELDS)
        );
    }

    /**
     * @inheritDoc
     */
    public function getValidationRuleForCreate(): ParamRuleCollection
    {
        return new ParamRuleCollection(
            ...$this->getCommonBodyValidationRules()
        );
    }

    /**
     * @return ParamRule[]
     */
    private function getCommonBodyValidationRules(): array
    {
        return [
            new ParamRule(
                self::PARAMETER_NAME,
                new Rule(Rules::STRING_TYPE),
                new Rule(Rules::LENGTH, [null, 100])
            ),
            $this->getValidationDecorator()->notRequiredParamRule(
                new ParamRule(
                    self::PARAMETER_DESCRIPTION,
                    new Rule(Rules::STRING_TYPE),
                    new Rule(Rules::LENGTH, [null, 255])
                )
            )
        ];
    }

    /**
     * @inheritDoc
     */
    public function getValidationRuleForUpdate(): ParamRuleCollection
    {
        return new ParamRuleCollection(
            new ParamRule(
                CommonParams::PARAMETER_ID,
                new Rule(Rules::POSITIVE)
            ),
            ...$this->getCommonBodyValidationRules()
        );
    }

    /**
     * @inheritDoc
     */
    public function getValidationRuleForDelete(): ParamRuleCollection
    {
        $paramRules = new ParamRuleCollection();

        if ($allowed) {
            $paramRules->addParamValidation(
                new ParamRule(
                    CommonParams::PARAMETER_IDS,
                    new Rule(Rules::ARRAY_TYPE)
                )
            );
        }
        return $paramRules;
    }
}

Clone this wiki locally