Installation
composer require b2pweb/bdf-prime-bundle
Add to config/bundles.php:
Bdf\PrimeBundle\PrimeBundle::class => ['all' => true],
Bdf\PrimeBundle\TestingPrimeBundle::class => ['test' => true],
Configure .env
DATABASE_URL=mysql://user:pass@host/dbname?serverVersion=5.7
Basic Config (config/packages/prime.yaml)
prime:
activerecord: true
connections:
default: '%env(resolve:DATABASE_URL)%'
First Use Case: Define a Model
// src/Model/User.php
namespace App\Model;
use Bdf\Prime\ActiveRecord\Model;
class User extends Model
{
public static string $table = 'users';
public ?int $id;
public string $name;
public string $email;
}
Usage:
$user = User::find(1);
$user->save();
CRUD Operations
// Create
$user = new User();
$user->name = 'John Doe';
$user->email = 'john@example.com';
$user->save();
// Read
$user = User::find(1);
$users = User::all();
// Update
$user->name = 'Updated Name';
$user->save();
// Delete
$user->delete();
Query Builder
$activeUsers = User::where('active', true)->orderBy('name')->get();
$count = User::count();
Relationships
// One-to-Many
class Post extends Model
{
public static string $table = 'posts';
public ?int $user_id;
public string $title;
public User $user; // BelongsTo
}
class User extends Model
{
public static string $table = 'users';
public ?int $id;
public string $name;
public array $posts = []; // HasMany
}
Transactions
User::transaction(function () {
$user = new User();
$user->name = 'Alice';
$user->save();
$post = new Post();
$post->title = 'Hello';
$post->user_id = $user->id;
$post->save();
});
Hybrid Approach
Use activerecord: false in prime.yaml and manually map models to Doctrine entities:
prime:
activerecord: false
hydrators: '%kernel.cache_dir%/prime/hydrators/loader.php'
Custom Hydrators Override hydrators for complex mappings:
// src/Prime/Hydrator/UserHydrator.php
namespace App\Prime\Hydrator;
use Bdf\Prime\Hydrator\HydratorInterface;
use App\Model\User;
class UserHydrator implements HydratorInterface
{
public function hydrate(array $data): User
{
$user = new User();
$user->id = $data['id'] ?? null;
$user->name = $data['name'] ?? '';
$user->email = $data['email'] ?? '';
return $user;
}
}
Event Listeners
// src/EventListener/UserListener.php
namespace App\EventListener;
use Bdf\Prime\Event\UserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class UserListener implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
UserEvent::PRE_SAVE => 'onPreSave',
];
}
public function onPreSave(UserEvent $event): void
{
$user = $event->getUser();
$user->email = strtolower($user->email);
}
}
Test Configuration
# config/packages/test/prime.yaml
prime:
logging: true
cache:
query: { pool: null, service: null }
metadata: { pool: null, service: null }
Test Fixtures
// tests/PrimeFixtures/UserFixtures.php
namespace App\Tests\PrimeFixtures;
use Bdf\Prime\Fixture\FixtureInterface;
use App\Model\User;
class UserFixtures implements FixtureInterface
{
public function load(): void
{
$user = new User();
$user->name = 'Test User';
$user->email = 'test@example.com';
$user->save();
}
}
Unit Tests
// tests/Unit/UserTest.php
use App\Model\User;
use PHPUnit\Framework\TestCase;
class UserTest extends TestCase
{
public function testSaveUser(): void
{
$user = new User();
$user->name = 'Test';
$user->email = 'test@example.com';
$user->save();
$this->assertNotNull($user->id);
}
}
Connection Issues
DATABASE_URL in .env matches your database schema (e.g., mysql://user:pass@host/dbname).sqlite::memory: and ensure the prime.yaml test config disables caching.Caching Quirks
prime:
cache:
query:
service: 'Bdf\Prime\Cache\ArrayCache'
metadata:
pool: 'cache.app'
prime:
cache:
query: { pool: null, service: null }
metadata: { pool: null, service: null }
Active Record vs. Data Mapper
activerecord: true, models are tightly coupled to the database schema. For flexibility, set activerecord: false and use custom hydrators.Migration Path Conflicts
migration.path in prime.yaml points to a valid directory:
prime:
migration:
path: '%kernel.project_dir%/src/Migration'
php bin/console prime:migrate
Enable Logging
prime:
logging: true
Check logs in var/log/dev.log for SQL queries and errors.
Query Inspection
Use the PrimeBundle profiler to inspect executed queries:
// In a controller or command
$query = User::where('active', true);
dump($query->getQuery()->getSql());
Hydrator Debugging Override hydrators to log data:
public function hydrate(array $data): User
{
error_log(print_r($data, true)); // Debug raw data
return parent::hydrate($data);
}
Custom Query Builders Extend the query builder for domain-specific methods:
// src/Prime/Query/UserQueryBuilder.php
namespace App\Prime\Query;
use Bdf\Prime\Query\QueryBuilder;
class UserQueryBuilder extends QueryBuilder
{
public function active(): self
{
return $this->where('active', true);
}
}
Event Dispatching Subscribe to events for pre/post hooks:
// src/EventSubscriber/UserSubscriber.php
use Bdf\Prime\Event\UserEvent;
class UserSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
UserEvent::PRE_SAVE => 'onPreSave',
UserEvent::POST_SAVE => 'onPostSave',
];
}
}
Custom Fixtures Load test data dynamically:
// tests/PrimeFixtures/LoadFixtures.php
namespace App\Tests\PrimeFixtures;
use Bdf\Prime\Fixture\FixtureLoader;
class LoadFixtures
{
public function __invoke(): void
{
(new FixtureLoader())->load([new UserFixtures()]);
}
}
insert() for bulk inserts:
User::insert([
['name' => 'User
How can I help you explore Laravel packages today?