Installation
composer require sylius/resource-bundle
Add the bundle to config/bundles.php:
return [
// ...
Sylius\Bundle\ResourceBundle\SyliusResourceBundle::class => ['all' => true],
];
Define a Resource
Create a Resources/config/doctrine/YourEntity.orm.yml (or equivalent for ODM):
Sylius\Bundle\ResourceBundle\Doctrine\ORM\ResourceEntityRepository:
arguments:
$entityClass: App\Entity\YourEntity
Register the Resource
In config/packages/sylius_resource.yaml:
sylius_resource:
resources:
app.your_entity:
driver: doctrine/orm
classes:
model: App\Entity\YourEntity
repository: Sylius\Bundle\ResourceBundle\Doctrine\ORM\ResourceEntityRepository
First Controller
Extend ResourceController in src/Controller/YourEntityController.php:
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
class YourEntityController extends ResourceController
{
public function __construct(
YourEntityRepository $repository,
YourEntityManagerInterface $manager
) {
parent::__construct($repository, $manager);
}
}
Route the Controller
In config/routes.yaml:
app_your_entity:
resource: |
alias: app.your_entity
section: admin
templates: "@SyliusResourceBundle:Crud"
except: ['new', 'create']
path: /admin/your-entities
type: sylius.resource
/admin/your-entities (automatically paginated, sortable, filterable)./admin/your-entities/{id}./admin/your-entities/{id} with _method=_put or X-HTTP-Method-Override./admin/your-entities/{id}.Custom Queries: Extend ResourceEntityRepository to add methods:
public function findByActive(bool $active): array
{
return $this->createQueryBuilder('o')
->andWhere('o.active = :active')
->setParameter('active', $active)
->getQuery()
->getResult();
}
Use in controller:
$this->repository->findByActive(true);
Criteria-Based Filtering:
use Sylius\Bundle\ResourceBundle\Criteria\Criteria;
$criteria = new Criteria();
$criteria->add('name', 'LIKE', '%search%');
$this->repository->matching($criteria);
Override Actions:
public function indexAction(): Response
{
$this->repository->addFilter('active', true);
return parent::indexAction();
}
Pre/Post Hooks:
protected function preUpdate(YourEntityInterface $entity): void
{
$entity->setUpdatedAt(new \DateTime());
}
Serialization Groups:
Configure in config/packages/serializer.yaml:
sylius_resource:
resources:
app.your_entity:
serialization_groups: [api]
Use in entity:
#[Groups(['api'])]
private string $name;
API Platform Compatibility:
use Sylius\Bundle\ResourceBundle\Controller\ApiPlatformResourceController;
class YourEntityController extends ApiPlatformResourceController
{
// Extend for custom logic
}
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
$dispatcher->addListener(
ResourceControllerEvent::PRE_CREATE,
function (ResourceControllerEvent $event) {
$event->getSubject()->setCreatedBy($this->getUser());
}
);
sylius_resource:
resources:
app.your_entity:
driver: doctrine/orm
classes:
repository: App\Repository\YourEntityRepository
app.your_entity_archive:
driver: doctrine/orm
classes:
repository: App\Repository\YourEntityArchiveRepository
Reuse controllers with different repositories.Repository Injection:
ResourceController and pass the correct repository in the constructor:
public function __construct(YourEntityRepository $repository, YourEntityManagerInterface $manager)
{
parent::__construct($repository, $manager);
}
Filter Persistence:
indexAction persist across requests if not cleared.$this->repository->removeAllFilters();
ORM vs. ODM:
driver correctly in sylius_resource.yaml.Serialization Conflicts:
password).#[Groups] attributes.Caching Headaches:
matching(Criteria) results not cached.CacheableInterface in your repository or use Cache component manually.Enable SQL Logging:
# config/packages/dev/doctrine.yaml
doctrine:
dbal:
logging: true
profiling: true
Criteria Debugging: Dump criteria before execution:
$criteria = $this->repository->getUsedCriteria();
dump($criteria->getFilters());
Event Dispatcher:
Check fired events in PRE_*/POST_* hooks:
$dispatcher->addListener(
ResourceControllerEvent::PRE_CREATE,
function (ResourceControllerEvent $event) {
error_log('Creating: ' . $event->getSubject()->getId());
}
);
Custom Managers:
Implement ManagerInterface for non-Doctrine backends (e.g., Elasticsearch):
class ElasticsearchManager implements ManagerInterface
{
public function createNew(): YourEntityInterface
{
return new YourEntity();
}
public function persist(YourEntityInterface $entity): void
{
// Elasticsearch logic
}
}
Dynamic Routes:
Use sylius.resource route type with dynamic segments:
app_your_entity_show:
path: /your-entities/{id}/{slug}
methods: GET
defaults:
_controller: app.your_entity.controller:showAction
_sylius:
resource: app.your_entity
id: id
criteria:
slug: =context.get('slug')
Bulk Actions:
Extend ResourceController to handle bulk operations:
public function bulkUpdateAction(Request $request): Response
{
$ids = $request->request->get('ids', []);
$entities = $this->repository->findBy(['id' => $ids]);
foreach ($entities as $entity) {
$entity->setActive(false);
$this->manager->persist($entity);
}
$this->manager->flush();
return new JsonResponse(['success' => true]);
}
Driver Mismatch:
driver in sylius_resource.yaml matches your setup (e.g., doctrine/orm, doctrine/mongodb).Repository Aliasing:
sylius_resource:
resources:
app.your_entity:
driver: doctrine/orm
classes:
repository: App\Repository\YourEntityRepository
archive_repository: App\Repository\YourEntityArchiveRepository
Template Overrides:
templates/SyliusResourceBundle/Crud/ (e.g., index.html.twig).templates option in routes:
templates: "@App/Crud"
Pagination Defaults:
How can I help you explore Laravel packages today?