AtroCore utilizes its own built-in ORM (object-relational mapping) to interact with the database or files (in case of ReferenceData). Create, update, read, delete, restore, delete permanently and search operations are performed via the Repository instance provided by the entity manager instance.

You can get an instance of the Entity Manager using the container or inject it directly.

We will continue to use the entity we create in the section Entity.

Repository

AtroCore use the repository pattern to manage entities, so each entity has it own repository, and every repository extended \Atro\Core\Templates\Repositories{Type} where Type is Base, Hierarchy, Relation or ReferenceData to get a repository

/** @var \Espo\Core\ORM\EntityManager $entityManager */
$entityManager = $containter->get('entityManager');

/** @var \Atro\Core\Templates\Repositories\Base $repository */
$repository = $entityManager->getRepository('Manufacturer');

CRUD operations

Create

/** @var \Espo\Core\ORM\EntityManager $entityManager */
$entityManager = $containter->get('entityManager');

/** @var \Atro\Core\Templates\Entities\Base $manufacturer */
$manufacturer =  $entityManager->getRepository('Manufacturer')->get();
//Or
$manufacturer = $entityManager->getEntity('Manufacturer');

// set value
$manufacturer->set('name', 'Manufacturer name');

 $entityManager->getRepository('Manufacturer')->save($manufacturer);
 // Or
 $entityManager->saveEntity($manufacturer);

Read and Update

$entityManager = $containter->get('entityManager');

$manufacturer =  $entityManager->getRepository('Manufacturer')->get($id);
// Or
$manufacturer = $entityManager->getEntity('Manufacturer', $id);

$manufacturer->set('name', 'New Manufacturer name');

$entityManager->getRepository('Manufacturer')->save($manufacturer);
// Or
$entityManager->saveEntity($manufacturer);

Delete

$entityManager = $containter->get('entityManager');

$manufacturer = $entityManager->getEntity('Manufacturer', $id);

$entityManager->getRepository('Manufacturer')->remove($entity);

// Or
$entityManager->removeEntity($manufacturer);

Restore

$entityManager = $containter->get('entityManager');

$entityManager->getRepository('Manufacturer')->restore($id);

Delete Permanently

$entityManager = $containter->get('entityManager');

$entityManager->getRepository('Manufacturer')->deleteFromDb($id);

echo $manufacturer->get('name');
// new Manufacturer name

List

$entityManager = $containter->get('entityManager');

$manufacturers = $entityManager->getRepository('Manufacturer')->find();
$manufacturerCount = $entityManager->getRepository('Manufacturer')->count();

Select fields

$entityManager = $containter->get('entityManager');

$manufacturers = $entityManager->getRepository('Manufacturer')->select(['id', 'name'])->find();

Ordered list

$manufacturers = $entityManager->getRepository('Manufacturer')
    ->limit(0, 10)
    ->order('createdAt', 'DESC')
    ->find();

Find the first one

$manufacturers = $entityManager->getRepository('Manufacturer')->findOne();
 $manufacturer = $entityManager->getEntity('Manufacturer', $id);
 $relationNames = 'products';
 $products = $entityManager->getRepository('Manufacturer')->findRelated($entity, $relationNames);

Querying

Comparison operators

Supported comparison operators are: >, <, >= <=, =, !=.

$stocks = $entityManager->getRepository('Stock')->where(['amount>=' => 150])->find();

When using = use can omit it.

IN and NOT IN

Here the operators =, != are still used, the value just need to be and array.

 $manufacturers = $entityManager->getRepository('Manufacturer')->where(['name' => ['ManufacturerName1', 'ManufacturerName2']]);
 $notInManufacturers = $entityManager->getRepository('Manufacturer')->where(['name!=' => ['ManufacturerName1', 'ManufacturerName2']]);

LIKE operators

* is used for LIKE and !* for NOT LIKE

 $manufacturers = $entityManager->getRepository('Manufacturer')->where(['name*' => '%atrocore%' ]);

OR, AND operators

$opportunityList = $entityManager
    ->getRepository('Product')
    ->where([
        [
            'OR' => [
                ['status' => 'draft'],
                ['isActive' => false],
            ],
            'AND' => [
                'quantity>' => 100,
                'quantity<=' => 999,
            ],
        ]
    ])
    ->findOne();

Customizing an AtroCore Repository

When developing with AtroCore, you may need to add custom methods to a repository to reuse complex database queries or modify the default behavior of entity interactions.

To create a custom repository for an entity, you need to create a new class in your module's Repositories directory. This class should extend the appropriate base repository class, which is determined by the entity's type (e.g., Base, Hierarchy, ReferenceData, or Relation).

For our Manufacturer entity from the ExampleModule, the custom repository file would be located at Repositories/Manufacturer.php.

The class should extend \Atro\Core\Templates\Repositories\Base, as the Manufacturer entity is of the Base type.

<?php

namespace ExampleModule\Repositories;

use Atro\Core\Templates\Repositories\Base;
use Espo\ORM\EntityCollection;
use Espo\ORM\Entity;

class Manufacturer extends Base
{
    /**
     * Returns all active manufacturers.
     *
     * @return EntityCollection
     */
    public function getActiveManufacturers(): EntityCollection
    {
        return $this->where(['isActive' => true])->find();
    }

    /**
     * Capitalizes the manufacturer's name before it is saved.
     *
     * @param Entity $entity
     * @param array $options
     */
    protected function beforeSave(Entity $entity, array $options = [])
    {
        $entity->set('name', ucfirst($entity->get('name')));
        parent::beforeSave($entity, $options);
    }
}

This custom repository now includes two new methods:

  • getActiveManufacturers(): A new method to retrieve all active manufacturers using a simple where clause.
  • beforeSave(): A protected method that overrides the default behavior to capitalize the manufacturer's name before the entity is saved to the database. This allows you to apply consistent data formatting. There are more method like afterSave, beforeRemove, afterRemove, beforeRestore, afterRestore,...

Using the Custom Repository

Once the custom repository is created, you can access it through the EntityManager and call your new methods.

/** @var \Espo\Core\ORM\EntityManager $entityManager */
$entityManager = $containter->get('entityManager');

/** @var \ExampleModule\Repositories\Manufacturer $repository */
$repository = $entityManager->getRepository('Manufacturer');

$activeManufacturers = $repository->getActiveManufacturers();

Using a low level Connection instance:

/** @var \Espo\Core\ORM\EntityManager $entityManager */
$entityManager = $containter->get('entityManager');

/** @var \Doctrine\DBAL\Connection $connection */
$connection = $entityManager->getConnection();

$activeManufacturers = $connection->createQueryBuilder()
    ->from($connection->quoteIdentifier('manufacturer'))
    ->select('id, name')
    ->where('deleted = :false')
    ->andWhere('isActive = :true')
    ->setParameter('false', false, \Doctrine\DBAL\ParameterType::BOOLEAN)
    ->setParameter('true', true, \Doctrine\DBAL\ParameterType::BOOLEAN)
    ->fetchAllAssociative();

Conclusion

The entityManager in AtroCore is used to manage entities, but its built-in repository for querying entities has limitations, especially when it comes to querying on related fields.

For more advanced or complex queries that involve relational data, you need to use the SelectManager. This tool provides more powerful querying capabilities beyond what the standard repository offers.

You can learn more about how to perform complex queries with the SelectManager in the Advanced data querying section.