Installation Add the bundle via Composer:
composer require dayspring-tech/propel-bundle
Enable it in config/bundles.php:
return [
// ...
Dayspring\PropelBundle\PropelBundle::class => ['all' => true],
];
Configuration
Update config/packages/propel.yaml (auto-generated on first run):
propel:
dbal:
driver: "%env(DATABASE_DRIVER)%"
host: "%env(DATABASE_HOST)%"
dbname: "%env(DATABASE_NAME)%"
user: "%env(DATABASE_USER)%"
password: "%env(DATABASE_PASSWORD)%"
port: "%env(DATABASE_PORT)%"
Run schema initialization:
php bin/console propel:schema:init
First Use Case Generate a model from an existing table:
php bin/console propel:model:build --schema=your_schema
Use the model in a controller:
use App\Models\YourModel;
public function index()
{
$records = YourModelQuery::create()->find();
return $this->render('template.html.twig', ['records' => $records]);
}
Schema Management
propel:schema:diff and propel:schema:update for version-controlled schema changes.php bin/console propel:model:build --schema=public --output-dir=src/Models
Querying Data
$activeUsers = UserQuery::create()
->filterByIsActive(true)
->orderByLastLoginDesc()
->find();
findOneBy(), findBy(), or create() for direct hydration.Relationships
getX() (e.g., $user->getPosts()).with():
$users = UserQuery::create()->with('Posts')->find();
Transactions Wrap operations in a transaction:
$conn = Propel::getConnection();
$conn->beginTransaction();
try {
$user->save();
$post->save();
$conn->commit();
} catch (\Exception $e) {
$conn->rollBack();
throw $e;
}
Custom Queries
Extend BaseQuery for reusable logic:
class UserQuery extends BaseUserQuery
{
public function activeAndRecent()
{
return $this->filterByIsActive(true)->filterByCreatedAt('>=', date('Y-m-d', strtotime('-30 days'))));
}
}
ModelType:
use Dayspring\PropelBundle\Form\ModelType;
$builder->add('user', ModelType::class, [
'class' => User::class,
'property' => 'profile',
]);
@ApiResource with Propel entities (requires custom hydration).postSave, postDelete) via Symfony’s event dispatcher.Schema Locking
schema:update runs.--no-lock for testing (not production):
php bin/console propel:schema:update --no-lock
Model Generation Overwrites
Base*Query or Base*Peer classes.src/Models/ (auto-loaded) instead of generated-models/.Caching Quirks
php bin/console cache:clear
# config/packages/propel.yaml
propel:
runtime:
cache: false
Relationship Ambiguity
localField and foreignField in schema.xml:
<column name="user_id" type="integer" required="true" primaryKey="false" foreignTable="users" foreignReference="id" />
Symfony Dependency Injection
Connection is not a Symfony service by default. Bind it manually:
# config/services.yaml
services:
App\PropelConnection:
class: Propel\Runtime\Connection\ConnectionWrapper
factory: ['@propel.connection', 'getConnection']
Enable SQL Logging:
# config/packages/propel.yaml
propel:
runtime:
log: true
Logs appear in var/log/propel.log.
Query Debugging:
Use ->debug() to dump SQL:
$query = UserQuery::create()->filterByName('John');
$query->debug(); // Outputs SQL to console
Common Errors:
propel:schema:init or check schema.xml.schema.xml.schema.xml.Custom Behavior
Override Base*Query methods (e.g., postInsert()) for hooks:
class UserQuery extends BaseUserQuery
{
protected function postInsert($con, $obj)
{
// Add custom logic after insert
}
}
Schema Customization
Extend schema.xml in config/propel/schema.xml for global changes.
Event Listeners Listen to Propel events via Symfony’s event system:
// src/EventListener/PropelListener.php
public static function getSubscribedEvents()
{
return [
PropelEvents::POST_SAVE => 'onPostSave',
];
}
Custom Validators
Add validation in Base*Peer:
class BaseUserPeer
{
public static function validate($con, $obj)
{
if (strlen($obj->getEmail()) > 255) {
throw new \PropelException('Email too long');
}
}
}
How can I help you explore Laravel packages today?