Installation
composer require backsystem/base
Add the bundle to config/bundles.php:
Backsystem\Base\BaseBundle::class => ['all' => true],
First Use Case: Doctrine Entity Base
Extend BaseEntity for automatic timestamping, soft deletes, and UUID support:
use Backsystem\Base\Entity\BaseEntity;
#[ORM\Entity]
class User extends BaseEntity
{
#[ORM\Column]
private string $name;
}
createdAt, updatedAt fields (via BaseEntity traits)BaseBundle config)SoftDeletable trait)Configuration
Override defaults in config/packages/backsystem_base.yaml:
backsystem_base:
use_uuid: true
soft_deletes: true
default_locale: 'en'
Traits for Reusability Combine traits in your entities for modular behavior:
use Backsystem\Base\Entity\Traits\{
SoftDeletable,
Sluggable,
Translatable
};
class Product extends BaseEntity
{
use SoftDeletable, Sluggable;
}
slug from name (configurable separator).name_en, name_fr).Repository Integration
Extend BaseRepository for common CRUD operations:
use Backsystem\Base\Repository\BaseRepository;
class UserRepository extends BaseRepository
{
public function findByEmail(string $email): ?User
{
return $this->createQueryBuilder('u')
->where('u.email = :email')
->setParameter('email', $email)
->getOneOrNullResult();
}
}
paginate() with KnpPaginatorBundle:
$users = $this->paginate(User::class, $page, 10);
BaseFormType for rapid form creation with built-in validation:
use Backsystem\Base\Form\BaseFormType;
class UserType extends BaseFormType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, [
'constraints' => [new NotBlank(), new Length(['max' => 100])],
])
->add('email', EmailType::class);
}
}
use Backsystem\Base\Message\SendEmail;
$this->messageBus->dispatch(new SendEmail(
'user@example.com',
'Welcome!',
'email_template.html.twig'
));
SendEmailHandler) and tag them with @asynchronous.Translatable trait for multi-language support:
class Product extends BaseEntity
{
use Translatable;
#[ORM\Column]
private string $name; // Base field
#[ORM\Column(nullable: true)]
private ?string $nameEn = null;
#[ORM\Column(nullable: true)]
private ?string $nameFr = null;
}
{{ product.name(currentLocale) }}.{{ dump(base_entity.getUuid()) }}
{{ base_form.hasErrors(form) ? 'Error!' : '' }}
{{ knp_pagination_render(paginator) }}
UUID vs. Auto-Increment
use_uuid: true is set but your database lacks a uuid-ossp extension, fall back to auto-increment:
backsystem_base:
use_uuid: false # Override in production
Soft Deletes in Queries
// ❌ Missing scope
$user = $entityManager->getRepository(User::class)->find(1);
// ✅ Correct (uses BaseRepository's soft-delete scope)
$user = $entityManager->getRepository(User::class)->findOneBy(['id' => 1]);
Translation Locale Mismatch
default_locale in config matches your app’s locale (e.g., en vs. en_US).Messenger Transport Configuration
.env:
MESSENGER_TRANSPORT_DSN=doctrine://default
Enable SQL Logging
Add to config/packages/dev/doctrine.yaml:
dbal:
logging: true
profiling: true
Check Entity Changes
Use BaseEntity's getChangedFields() to debug updates:
$user->setName('New Name');
dump($user->getChangedFields()); // ['name' => ['old' => 'Old Name', 'new' => 'New Name']]
Custom Traits
Extend BaseEntity with your own traits (e.g., AuditLoggable):
namespace App\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
trait AuditLoggable
{
#[ORM\Column(type: 'json')]
private array $auditLog = [];
public function logChange(string $action, string $field, $oldValue, $newValue): void
{
$this->auditLog[] = compact('action', 'field', 'oldValue', 'newValue');
}
}
Override Form Types
Customize BaseFormType behavior:
class CustomUserType extends BaseFormType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_token_id' => 'custom_token', // Override CSRF token
]);
}
}
Event Subscribers
Listen to BaseEntity lifecycle events:
use Backsystem\Base\Event\BaseEntityEvents;
$dispatcher->addListener(BaseEntityEvents::PRE_PERSIST, function (BaseEntityEvent $event) {
if ($event->getEntity() instanceof User) {
$event->getEntity()->setEmail(strtolower($event->getEntity()->getEmail()));
}
});
BaseRepository::bulkUpdate() for large datasets:
$this->bulkUpdate(
User::class,
['status' => 'active'],
['id' => [1, 2, 3]]
);
N+1 issues by eager-loading associations:
$users = $this->findBy(['role' => 'admin'], ['posts' => 'id']);
How can I help you explore Laravel packages today?