I'm picking up PHP Active Record and dealing with associations. I have two related objects using a "Has_Many" and "Belongs_to" (parent/child) and trying to create a child record when creating a new parent record (in this case creating a "skin" for my "unit").
class Unit extends ActiveRecord\Model
{
static $has_many = array(
array('skins')
);
}
class Skin extends ActiveRecord\Model
{
static $belongs_to = array(
array('unit')
);
}
I've found both of these threads here: http://www.phpactiverecord.org/boards/4/topics/153 Activerecord-association: create new object (find class)
So I my code currently looks like:
$unit = new Unit();
$unit->name = 'somename';
$unit->description = 'somedescription';
$skinArray = array('name' => $unit->name.' Default Skin');
$unit->create_skins($skinArray);
$unit->save();
The code above is not associating the new skin to the unit in the database or in code though it /is/ placing a new skin record in the database (with a unit_id of NULL). Using "build_skins" doesn't put a Skin record in the database.
I was hoping there was a way to add a "child" to the parent model via the model itself as some other ORM's do. The only way I can do this is to do it explicitly:
$unit = new Unit();
$skin = new Skin();
$unit->name = 'somename';
$unit->description = 'somedescription';
$unit->save();
$skin->unit_id = $unit->id;
$skin->name = $unit->name.' Default Skin';
$skin->save();
Perhaps that is the way it is supposed to be done in PHP ActiveRecord and my expectations are wrong. But I was hoping for a way to do it through the objects that didn't require saving the Parent to the DB with an explicit call first. For example the "Recess" PHP framework would have a simple call on the unit like such: $unit->addSkin($skin);
Do you configurate database connection? Look at gihub examples.
<?php
require_once __DIR__ . '/../../ActiveRecord.php';
class Book extends ActiveRecord\Model
{
// explicit table name since our table is not "books"
static $table_name = 'simple_book';
// explicit pk since our pk is not "id"
static $primary_key = 'book_id';
// explicit connection name since we always want production with this model
static $connection = 'production';
// explicit database name will generate sql like so => db.table_name
static $db = 'test';
}
$connections = array(
'development' => 'mysql://invalid',
'production' => 'mysql://test:[email protected]/test'
);
// initialize ActiveRecord
ActiveRecord\Config::initialize(function($cfg) use ($connections)
{
$cfg->set_model_directory('.');
$cfg->set_connections($connections);
});
print_r(Book::first()->attributes());
?>
You have to init DB to do this ActiveRecord\Config::initialize
.
Depending on your keys, should you not auto-increment (without NULL as the deafult value). I would take a look at the db key defualt values and adjust auto-increment or the default value of unit_id if it fits with your schema. You said the association is working with the two step save. Is the unit_id saved correctly in the two step save?
The docs are not very helpful as they recommend the explicit save you're trying to avoid.
@outrightmental has something interesting about setting unit->id with a hook, but wouldn't it need to be a before_create callback, you wouldn't want to insert records without the associations working. see this active record callbacks doc.
$after_create = array('validate association and assign unit_id if NULL');
or
$before_create = array('explcitly assign unit_id');
? the callback docs offer up other suggestions
I recommend implementing the method in an afterSave()
hook inside your parent model class, or in a custom model method createChild($data)
.
However (e.g. Yii Framework's CActiveRecord) something like the following is nice:
class Unit extends ActiveRecord\Model
{
...
public function afterSave() {
if ($this->isNewRecord) {
$firstSkin = new Skin();
$firstSkin->unitId = $this->id;
$firstSkin->name = $this->name . ' Default Skin';
$firstSkin->save();
}
}
...
}
If in fact the particular behavior is unique to the controller, then perhaps use the following alternative (data passed in from controller to create child):
class Unit extends ActiveRecord\Model
{
...
public function createSkin($data) {
$firstSkin = new Skin();
$firstSkin->unitId = $this->id;
$firstSkin->setAttributes($data);
$firstSkin->save();
}
...
}
Hope that helps. I'm a fan of ActiveRecord for quick scaffolding, although beware relying on it too "magically" for larger applications. Btw iz very nice in Rails too ;) where it originated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With