Introduction
Windwalker Simple ORM is a tool to configure relations between different tables, it helps JTable
and DataMapper
ables to operate other relative tables and data.
One to Many
If we have a table brands
relation to cars
and bikes
, the cars
and bikes
both contains a column brand_id
,
this is one-to-many
relation.
We can configure this relation in Windwalker Table
class:
<?php
use Windwalker\Relation\Action;
use Windwalker\Table\Table;
class VehicleTableBrand extends Table
{
public function __construct()
{
parent::__construct('#__flower_brands');
}
protected function configure()
{
// Configure One to Many Relations
$this->_relation->addOneToMany('cars') // Property to store relation data
// Use another JTable to handle CRUD, and the foreign key mapping.
->targetTable(new VehicleTableCar, array('id' => 'brand_id'))
->onUpdate(Action::CASCADE) // On update
->onDelete(Action::CASCADE); // On delete
$this->_relation->addOneToMany('roses')
->targetTable(new VehicleTableBike, array('id' => 'brand_id'))
->onUpdate(Action::CASCADE)
->onDelete(Action::CASCADE);
}
}
You can just send the table name to targetTable()
, the Table
object will be auto created.
->targetTable('#__vehicle_cars', array('id' => 'brand_id'))
CRUD
When VehicleTableBrand::load()
, all relative cars
and bikes
will also loaded.
$brandTable = JTable::getInstance('Brand', 'VehicleTable');
$brandTable->load(3);
// \Windwalker\Data\DataSet
foreach ($brandTable->cars as $car)
{
// \Windwalker\Data\Data
echo $car->title;
}
If we call VehicleTableBrand::store()
, all data in cars
and bikes
will also batch store back to database.
The store action was handled by VehicleTableCar
and VehicleTableBike
objects which we set when the relation configuring.
$brandTable->store(); // All cars and bikes data will be updated.
$brandTable->cars[] = new Data(array('title' => 'A new car'));
$brandTable->store(); // The latest inserted data will be created.
If we call VehicleTableBrand::delete()
, all relative data will auto be deleted too.
$brandTable->load(3);
$brandTable->delete(); // All cars and bikes relative to this brand item will be delete.
Actions
The relation handling above is CASCADE
, which means sync all data with update and delete action.
We can set onUpdate
and onDelete
to other actions.
// In VehicleTableBrand class
use Windwalker\Relation\Action;
// ...
$this->_relation->addOneToMany('cars')
->targetTable(new VehicleTableCar, array('id', 'brand_id'))
->onUpdate(Action::SET_NULL)
->onDelete(Action::SET_NULL);
Action | Description |
---|---|
Action::CASCADE |
When main record update or change the key value, all relative records' foreign keys will also change. If main record be deleted, all relative records will also be deleted too. |
Action::NO_ACTION or Action::RESTRICT |
If main record update, delete or change key value, all relative record will have no any actions. |
Action::SET_NULL |
If main record update, delete or change key value, all relative records' foreign key will be st to NULL |
See:
One to One
If a table members
and member_profiles
is one-to-one relation, and the member_profiles.member_id
matching the members.id
.
$this->_relation->addOneToOne('profile')
->targetTable(new Table('#__member_profiles'), array('id' => 'member_id'));
In this case, the loaded relative item will be a Data
object not DataSet
.
$memberTable->load(5);
$memberTable->profile; // \Windwalker\Data\Data
Many to One
We can set cars
belongs to brands
, this case is very similar to articles
and category
, both are many-to-one relation.
The key mapping must in turn.
// in VehicleTableCar class
$this->_relation->addManyToOne('brand')
->targetTable(new FlowerTableBrand, array('brand_id' => 'id'));
Now if we load a car record, we will get the matched brand item.
$carTable->load(4);
$carTable->brand; // \Windwalker\Data\Data
Many to One relation only support
read
now,delete
andstore
not works.
Many to Many
Many to many relation need a middleware table to support multiple mapping. If we have a members
and groups
is many-to-many relation,
then we must have a member_group_maps
table to handle this relation.
// In member table object
$this->_relation->addManyToMany('groups')
// We usually not create a JTable for mapping table, so just use table name at first argument.
->mappingTable('#__member_group_maps', array('id' => 'member_id'))
->targetTable(new FlowerTableGroup, array('group_id' => 'id'));
Flush
If our relation update is to delete all and re-create all, we can set it is flush
.
$this->_relation->addOneToMany('cars')
->targetTable(new VehicleTableCar, array('id', 'brand_id'))
->onUpdate(Action::SET_NULL)
->onDelete(Action::SET_NULL)
->flush(true); // Add this
Now if we call store()
, all relation data will be deleted and re-insert all data.
Relationship for DataMapper
If you hope the DataMapper
can also handle relationship, you must extends to AbstractObservableDataMapper
.
NOTE: In DataMapper, the relationship handler still use
Table
object.
use Windwalker\DataMapper\Adapter\DatabaseAdapterInterface;
use Windwalker\Table\Table;
class VehicleMapperBrand extends \Windwalker\DataMapper\ObservableDataMapper
{
public function __construct(DatabaseAdapterInterface $db = null)
{
parent::__construct('#__vehicle_brands', 'id', $db);
}
protected function initialise()
{
// Configure One to Many Relations
$this->_relation->addOneToMany('cars')
->targetTable(new VehicleTableCar, array('id' => 'brand_id'))
->onUpdate(Action::CASCADE)
->onDelete(Action::CASCADE);
}
}
$mapper = new VehicleMapperBrand;
$brands = $mapper->find(array('state' => 1));
foreach ($brands as $brand)
{
foreach ($brand->cars as $car)
{
$car->title;
}
}
Global Configuring
Sometimes we will hope we can only configure once and work on both Table
and DataMapper
, or maybe we need configure
relationship in different conditions, we can use RelationContainer
to make global configuring. When Table
and DataMapper
created, they will auto register these configurations.
// Set it in component class.
final class FlowerComponent extends \Flower\Component\FlowerComponent
{
public function prepare()
{
parent::prepare();
// Get RelationContainer and a Relation object
$relation = $this->container->get('relation.container')->getRelation('#__flower_sakuras');
$relation->addManyToOne('foo')
->targetTable(new FlowerTableFoo, array('foo_id' => 'id'));
$relation->addManyToOne('category')
->targetTable(JTable::getInstance('Category'), array('catid' => 'id'));
}
}
The $relation
which get from RelationContainer
is a singleton Relation
object for #__flower_sakuras
table.
All configurations we set to this table will be keep in RelationContainer
and latter if we create Table
or DataMapper
,
the relation configuration will auto register to these two objects.
Found a typo? Help us improve this document.
This document is for Windwalker Joomla RAD, if you are finding Windwalker PHP framework, please see: Windwalker Framework