This website requires JavaScript.

    RESTful services with unlimited nested criteria

    No coding is required to have RESTful services, you only need to Doctrine mappings. Once a new entity is created, it is automatically managed in the Laravel Admin and available in RESTful services.

    If you've read about Json Query, you might guess that creating services is very easy. Of course, Doctrine mapping provides a distinct advantage. Thanks to Doctrine, we know everything there is to know about entities. Using Doctrine mapping and Json Query, we can create fully automated services. Note that using the Doctrine mapping does not impose additional load on the servers, as there is no need to perform additional database queries to get information about entities. Moreover, we can configure the system to store mappings directly in RAM.

    Searching

    ORM Backend supports GET, POST and PUT request methods for searching. Any of them can be used of your choice. The supported media-types are application/json and application/x-www-form-urlencoded. The uri to send search request is /api/entities/{your-entity-name}. Where {your-entity-name} is a transformed full class name. See Json Query to read about names conversion and aliases. Of course you can implement your own controllers and do it very easily.

    /**
    *
    * @param  \Illuminate\Http\Request  $request
    * @param string $classUrlName
    * @return \Illuminate\Http\Response
    */
    public function search(Request  $request, string $classUrlName)
    {
        $class = Helper::classFromUlr($classUrlName);
        //$paginator = $this->paginator($this->repository->createQuery($class))->appends($request->all());
        $paginator = $this->cursor($this->repository->createQuery($class))->appends($request->all());
    
        return response()->json(new JsonCollectionSerializer($paginator, Helper::aliasFromClass($class)), 200);
    }

    Please note that features of this controller are:

    • Cleaning up the user input to prevent SQL injection.
    • Strong typing helps to identify most errors during the development phase.
    • Filtering and sorting by related associations with unlimited depth.
    • Selecting related associations with unlimited depth.
    • Only one query is executed in the database when using cursor, regardless of the nesting depth.
    • Access control. The output contains only the data that the current user has the right to read.
    • Result caching.

    Output

    The output contains only those associations that are explicitly specified in the select part of Json Query. You will receive only those that you demanded and nothing more.

    NULL values

    By default, there are no NULL values ​​or empty collections in the output. This behavior can be changed through configuration settings.

    Collections

    ORM Backend provides the ability to use collections in all parts of Json Query. But be careful if you intend to get a list of results that contains related collections. This can lead to unpredictable pagination results and excessive server load.

    Nested associations

    When selecting nested associations and collections, all parents must also be listed in the select part. Only root entity alias can be omitted.

    An exception will be thrown:

    {
        select: [
            "team.trainer.avatar",
            "team.players.avatar"
        ]
    }

    Correct:

    {
        select: [
            "team.trainer",
            "team.trainer.avatar",
            "team.players"
            "team.players.avatar"
        ]
    }

    Access control

    The default ACL implementation provides read-only access for all users to all entities. And only the super administrator can edit the entity data. But you can install the ormbackend/laravel-doctrine-acl package and get fully managed access control based on user group permissions. Read more about Access Control extended implementation.

    Caching

    The caching is optional. ORM Backend uses the result cache for queries with filtering, otherwise the Doctrine 2nd level cache is used.

    CRUD

    CRUD operations provided by the following URLs:

    ActionURIMethod
    Create/api/entities/{your-entity-name}/crudPOST
    Read/api/entities/{your-entity-name}/crud/{id}GET
    Update/api/entities/{your-entity-name}/crud/{id}PUT
    Delete/api/entities/{your-entity-name}/crud/{id}DELETE

    Partial updates are also supported.

    Validation

    ORM Backend offers two levels of validation. The first is to validate the request. This level can be used to check the file size, match passwords, etc. This level is optional and may not be implemented on most requests.

    The second level is the entity validation. It is always triggered by Doctrine lifecycle events. Regardless of whether an entity is changed from the web, api or console, the same validation rules will work. Such centralized validation and transactions ensure data integrity. To perform validation checks, you need to define validation rules for your entities.

    Here is the actual code of the Image entity that this site uses:

    <?php
    namespace App\Model;
    
    use Illuminate\Support\Facades\Storage;
    use OrmBackend\SoftDeleteable;
    use OrmBackend\Types\ImageType;
    
    class Image extends \App\Entities\Image implements SoftDeleteable, ImageType
    {
        /**
         *
         * {@inheritDoc}
         * @see \OrmBackend\ORM\Entities\Entity::getModelValidationRules()
         */
        public function getModelValidationRules()
        {
            return [
                'name' => ['nullable', 'string', 'max:255'],
                'path' => ['required', 'string', 'max:255'],
                'mimeType' => ['required', 'string', 'max:50'],
                'width' => ['required', 'integer', 'min:16'],
                'height' => ['required', 'integer', 'min:16'],
                'altText' => ['nullable', 'string', 'max:4000']
            ];
        }
        
        /**
         *
         * {@inheritDoc}
         * @see \OrmBackend\ORM\Entities\Entity::getRequestValidationRules()
         */
        static public function getRequestValidationRules()
        {
            return [
                'image' => ['required', 'image', 'max:2000']
            ];
        }
    }

    Adapters

    Sometimes we need to have a little more logic in the controller. ORM Backend provides an adapter interface for such cases. If you don't need to override some method, just return null from it. Then its default implementation will be used.

    interface ApiControllerAdapter
    {
        public function search(Request $request, string $classUrlName);
    
        public function create(Request $request, string $classUrlName);
    
        public function read(Request $request, string $classUrlName, $id);
    
        public function update(Request $request, string $classUrlName, $id);
    
        public function delete(Request $request, string $classUrlName, $id);
    }

    Once your implementation is ready, please don't forget to add it to the configuration.

    // ormbackend.php
    return [
        ...
        'adapters' => [
          'app-model-image' => OrmBackend\Adapters\ImageAdapter::class,
        ],
        ...
    ]
    

    Example of saving images

    public function create(Request $request, string $classUrlName)
    {
        $className = Helper::classFromUlr($classUrlName);
        $data = $request->json()->all();
        $request->validate($className::getRequestValidationRules());
        $data['name'] = $request->file('image')->getClientOriginalName();
        $data['path'] = $request->file('image')->store(config('ormbackend.upload.img'));
        
        if (!$data['path']) {
            throw ValidationException::withMessages([
                'image' => [__('Failed to store file.')],
            ]);
        }
        
        $instance = $this->repository->createOrUpdate($className, $data);
        $this->repository->em()->flush();
        
        return response()->json(new JsonSerializer($instance), 201);
    }