Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP ActiveRecord creating a child model via Has_Many association

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);

like image 483
SegFault Avatar asked Aug 02 '12 00:08

SegFault


3 Answers

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.

like image 55
Knase Avatar answered Nov 19 '22 11:11

Knase


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

like image 38
sjt003 Avatar answered Nov 19 '22 12:11

sjt003


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.

like image 27
Charney Kaye Avatar answered Nov 19 '22 13:11

Charney Kaye