Page 1 of 1

Doctrine CRUD

Posted: Wed Nov 18, 2009 5:53 am
by VladSun
:banghead:
Finally, I've decided to give it a try - Doctrine!
It looks nice, but ...
I tried to create a simple base CRUD Model (CodeIgniter):

Code: Select all

/**
 
 * @property string                 $tableName
 * @property Doctrine_Collection    $collection
 * @property Doctrine_Record        $record
 * @property Doctrine_Table         $table
 * 
 */
class CRUD_Model extends Model
{
    private     $recordClass= null;
    private     $table      = null;
    private     $collection = null;
    private     $record     = null;
 
    public function __construct($recordClass = '')
    {
        parent::__construct();
        $this->setRecordClass($recordClass);
    }
 
    public function setRecordClass($recordClass)
    {
        if (!empty($recordClass))
        {
            $this->recordClass = $recordClass;
            $this->table = Doctrine_Manager::connection()->getTable($this->recordClass);
        }
    }
 
    /**
     * Load the collection of objects.
     *
     * @access public
     * @return bool
     */
    public function load()
    {
        $query = Doctrine_Query::create()
            ->from($this->recordClass);
 
        $this->collection = $query->execute();
    }
 
    /**
     * Return the object collection
     *
     * @access public
     * @return Array
     */
    public function get()
    {
        return $this->collection->toArray();
    }
    /**
     * Add an object to the collection.
     *
     * @access public
     * @param mixed $data
     * @return bool
     */
    public function add($data)
    {
        $this->record = $this->table->create();
//      $this->record->assignIdentifier(null);
        $this->record->synchronizeWithArray($data);
        $this->record->save();
    }
 
    /**
     * Update the object in the collection.
     *
     * @access public
     * @return bool
     */
    public function update($data)
    {
        $this->record = $this->table->create();
        $this->record->assignIdentifier($data['id']);
        $this->record->synchronizeWithArray($data);
        $this->record->save();
    }
    /**
     * Remove an object from the collection
     *
     * @access public
     * @return bool
     */
    public function remove($data)
    {
        $this->record = $this->table->create($data);
        $this->record->assignIdentifier($data['id']);
        $this->record->delete();
    }
}
The Doctrine model I use (auto generated):

Code: Select all

abstract class RBaseCountry extends Doctrine_Record
{
    public function setTableDefinition()
    {
        $this->setTableName('country');
        $this->hasColumn('id', 'integer', 4, array(
             'type' => 'integer',
             'length' => 4,
             'unsigned' => 1,
             'primary' => true,
             'autoincrement' => true,
        ));
        $this->hasColumn('name', 'string', 25, array(
             'type' => 'string',
             'length' => 25,
             'fixed' => true,
             'primary' => false,
             'notnull' => true,
             'notblank' => true,
             'autoincrement' => false,
        ));
    }
 
    public function setUp()
    {
        parent::setUp();
        
        $this->hasMany('RProvince', array(
             'local' => 'id',
             'foreign' => 'country_id'));
    }
}
So ... why I need to explicitly set the PK of records by using assignIdentifier() in update/remove functions?!? Is it the only way it could be done?
Also, I tried to get the PK from the record by using record::identifier() but it was empty (before the assignIdentifier() call )?!? I'd suppose that table definition is enough to populate this array (at least it key values), especially when Table::create() is used ...

And last - if I try to use $this->record->assignIdentifier(null) in add() (so, that I could use a single save() method instead of two separate update/add methods) no creation of DB record happens.

Anybody (~josh :P)?

I really like how Doctrine works, but these things really annoyed me. I hope, it's my mistake somehow...

Re: Doctrine CRUD

Posted: Wed Nov 18, 2009 12:58 pm
by josh
If you instantiate your class now, its "id" property will be 0, if you load from the database it will be populated

Code: Select all

 
$obj = Doctrine::find( 52 ); // or whatever their API is, it's been a while
echo $obj->id; // should output 52
 
Doctrine is really good, I just didn't find it extensible enough personally. The database got too coupled to my code, among other pet peeves

Re: Doctrine CRUD

Posted: Wed Nov 18, 2009 1:07 pm
by VladSun
:(
I don't want to do this. I don't need redundant queries...

I don't understand why I can't set PK fields via setters (or fromArray(), etc.) ... is it really this way?
You may have noticed that my $data array passed to fromArray() contains the "id" field - that's what annoyed me ... :/

In fact, if I do echo $record->id it's OK - but $record->save() complains about unique constraint violation - it tries to INSERT (not UPDATE) because its identifier() array is empty ...

PS: It's good that the doctrine crew has put an one-click-view-source-button in their manual - it's really needed because of the "perfect" manual they give %$#%$#% :crazy:

Re: Doctrine CRUD

Posted: Wed Nov 18, 2009 5:17 pm
by VladSun
It's insane ...

Code: Select all

    public function update($data)
    {
        $this->record = $this->table->create();
        $this->record->assignIdentifier(array_intersect_key($data, array_flip($this->table->getIdentifierColumnNames())));
        $this->record->synchronizeWithArray($data);
        $this->record->save();
    }
and this DOESN'T work:

Code: Select all

    public function update($data)
    {
        $data['_identifier'] = array_intersect_key($data, array_flip($this->table->getIdentifierColumnNames()));
        $this->record = $this->table->create();
        $this->record->synchronizeWithArray($data);
        $this->record->save();
    }
Am I insane, or is IT insane?!?

Re: Doctrine CRUD

Posted: Wed Nov 18, 2009 7:14 pm
by josh
What is the assignIdentifier method? All you should need is to set properties and call save(). I don't see why you can't "create a setter" either?

Re: Doctrine CRUD

Posted: Thu Nov 19, 2009 5:05 am
by VladSun
josh wrote:What is the assignIdentifier method?
Obviously, this is a Record method to define and assign values to the PK fields of the Record.
josh wrote:All you should need is to set properties and call save().
Because:

Code: Select all

$record->id = $id;
DOESN'T assign values (nor even keys) to the data stored in Record::_id (that is the PK) protected member . So, save() doesn't know whether it's an UPDATE or INSERT operation.
josh wrote:I don't see why you can't "create a setter" either?
I can't understand what you mean.


I was hoping that Record::fromArray()&co. methods should handle assigning data to a Record in the very same way a Table::find() did - that's all what I want.

Well, it appears it's not the case.

Re: Doctrine CRUD

Posted: Thu Nov 19, 2009 5:39 am
by VladSun
The main problem I'm trying to solve is that this generic Model should get a Record instance according to PK data passed in the input data.
But because the Model doesn't know what's the PK structure it can't even perform a Table::find().

So, the only problem is how to perform a Table::find($record->get_record_PK_method_here());

While the Doctrine manual says that Record::identifier() is exactly the $record->get_record_PK_method_here(), methods used to set data to the fields, including the PK fields don't update the "identifier()" related data.

That's why

Record::fromArray($array);
Table::find(Record::identifier());

won't work.

Re: Doctrine CRUD

Posted: Thu Nov 19, 2009 6:09 pm
by josh
I think you're doing it wrong. You are supposed to have 1 subclass per record type, you are internally calling methods Doctrine is supposed to manage for you. Your "crud" object should use delegation not inheritance.

Did you try this: http://www.doctrine-project.org/documen ... st-project

Re: Doctrine CRUD

Posted: Thu Nov 19, 2009 7:00 pm
by VladSun
josh wrote:You are supposed to have 1 subclass per record type, you are internally calling methods
I do have 1 class per record.
josh wrote:you are internally calling methods Doctrine is supposed to manage for you.
Because I have no choice but to use them ... I know it seems to be a "dirty" solution - that is *exactly* why I started this thread.
josh wrote:Your "crud" object should use delegation not inheritance.
It's not an *ORM* CRUD object - it's a "Model" object which has properties Record, Collection and Table (Doctrine objects) and use them to perform more complicated CRUD operations (e.g. batch CRUD on collections). No inheritance involved.
Again:

Code: Select all

   if ($userId === null) {
        $user = new User();
    } else {
        $user = $userTable->find($userId);
    }
... that's exactly the piece of code I want to avoid.

I want to have a simple:

Code: Select all

$user->merge($_REQUEST['user']);
and only if I call

Code: Select all

$user->refresh()
an SQL query is sent.

I don't see nothing wrong with it :(