DataMapper ORM


Troubleshooting

If you find you're having problems getting DataMapper to work for you, please check each of the following items for common problems.

  1. Be sure to check that your server meets the Server Requirements and that you have correctly followed the Installation Instructions.
  2. If you have not yet read through all the topics in the General Topics section of the Table of Contents, I recommend you do this now. There might be some small gem of information you haven't seen which will solve your issue. I'd also suggest comparing your code against the examples to ensure you are using DataMapper as it was designed.
  3. Common Issue: Check to see that you haven't accidentally overridden any Reserved Names. This is common mistake with relationships, and can lead to unusual behavior.
  4. Common Issue: Double check all of your relationships, taking extra care to make sure that both sides are configured correctly.
  5. Please read through the FAQs below, to help give you some ideas. A very useful tip for debugging is “How can I see the queries being generated?”.
  6. Don't forget to search the manual!

Getting Support

Primary support for Datamapper ORM is provided through the CodeIgniter Forums. There's a lot of very helpful people there, and I usually respond very quickly!

In the event you've tried the above but you're still having issues, you are welcome to contact me at the below address.

Please do not ask for help with messages that says “I tried doing X, it didn't work. Why?“, where X looks like an example from the manual. Between the manual and the forums, you should at least be able to provide some basic troubleshooting.

Please contact me directly only as a last resort as I prefer to respond on the forums directly, as then everyone can learn. You can also send me private messages through the forums.

You can often catch me on IRC, http://irc.freenode.net. Questions I receive via email that do not contain a link to a forum thread discussing the issue will not be dealt with!

Contact Info: JavaScript must be enabled to see my email.

FAQs

This is a small list of common issues you may encounter

Why do I see a bunch of 'SELECT * FROM table LIMIT 1' queries?

The first time a model is used on a request, DataMapper connects to the database server and loads in the columns for its table. These queries often look like this:

SELECT * FROM `users` LIMIT 1

These queries are super-fast. They usually take almost no time to process.

However, if you really want to eliminate them, you can do so, using the production cache. Please read the warning on that page.

Database Error: Unknown Column 'table.*' in 'field list'

CodeIgniter 1.6.x only!

If you're getting an error that looks very similar to the one below, it's because you need to update your CodeIgniter MySQL Driver with the latest version from the CodeIgniter downloads.

A Database Error Occurred

Error Number: 1054

Unknown column `table.*` in 'field list'

How to update your MySQL Driver:

  1. Get the correct version of the mysql_driver.php file from the CodeIgniter repository.
  2. Upload it to your CodeIgniter system/database/drivers/mysql folder.

That's it! The error should now be gone.

Why isn't MySQL using Transactions?

This is almost always caused by using a MySQL database, and not using the correct table type. Please see the MySQL website for more information on database table types.

Why is DataMapper returning objects that shouldn't be in my results?

CodeIgniter does not (as of this FAQ) support a way to easily group query clauses with parentheses. This can lead to unexpected results when combining a AND with an OR clause, because the AND has precedence. For example:

$job->where('new', TRUE)->or_where('open', TRUE)->where_related($user)->get();

You might expect this to return all new or open jobs by the User $user. However, the query looks similar to this:

SELECT * FROM `jobs`
WHERE `new` = 1 OR `open` = 1 AND `user_id` = 5
This means that the query will select all jobs that are new OR are both open AND assigned to $user.

However, Datamapper ORM does support query grouping. This means you can structure your query like this:

$job->group_start()->where('new', TRUE)->or_where('open', TRUE)->group_end()->where_related($user)->get();

And your query will now look similar to this:

SELECT * FROM `jobs`
WHERE (`new` = 1 OR `open` = 1) AND `user_id` = 5

Why do I get errors with non-numeric IDs?

DataMapper only works with automatically generated, numerical IDs.

There is no way around either limitation. If you need to store a unique value that is not serially generated, I recommend storing it in a different field on the table. You still need an ID column, but the other column can still be UNIQUE and indexed.

Note that you can now force an object with an ID to be inserted.

How can I see the queries being generated?

The CodeIgniter Profiler provides a lot of information about what was happening. This is useful for when there was no output.

Enabling the Profiler

// In Controller
$this->output->enable_profiler(TRUE);

In the event that the profiler has TOO much information, or you are outputting to a non-HTML format (such as JSON information), use $object->check_last_query(). (More detailed information.)

Getting the Last Generated Query

$object->where('thing', TRUE)->get();
$last_query = $object->check_last_query(FALSE, TRUE);

$last_query would contain something like:

SELECT `objects`.*
FROM `objects`
WHERE `objects`.`thing` = 1

Does Datamapper support modularity or multiple model folders?

1.8.1 and newer:

Datamapper supports model classes from CodeIgniter 2.0+ packages, HMVC Modular Extensions and Modular CI modules, or through the add_model_path static method.

Why can't I loop over this result set?

1.7.0 and newer:

The recommended method from this version on is to not specify the all array any more. If you are using get_iterated, you cannot specify the array, as it is not populated.

// recommended format.
foreach($object as $o) {
    echo $o->value;
}

1.4.1 and earlier:

You must loop over $object->all, not $object directly.

foreach($object->all as $o) {
    echo $o->value;
}

(From 1.4.2 on, you can loop over $object directly, and it will loop over the all array automatically.

Why is DataMapper generating the wrong table name?

While datamapper is very good at commonly pluralized english words, sometimes it stumbles on common words such as Address. In these cases, you must manually specify the table name, by adding $table (plural) and sometimes $model (singular) to the model definition.

For an example, see the Country model on the DataMapper Models page.

Many of these problems have been fixed with the updated inflector_helper, which comes with Datamapper ORM. If you have installed it, but still have issues, please contact me with the specific words (on the forum or above) and I'll try to update the inflector helper.

Why do I get an error saying {Field} must be a valid object or a string?

This error will occur whenever you have incorrectly configured your relationships. See this FAQ for more information.

Why do I get a Property or Method Does Not Exist when trying to access a model?

The most common reason for this is having the same name for both a Controller and a Model. Because DataMapper models are PHP Objects, they share the PHP namespace with the controllers. This means you cannot have a model named User and a controller also named User. Instead, it is recommended that you use plural names on the controllers.

What's that? You have a model named moose or fruit, or just really like the singular form? Well, you can solve this in several ways:

Why do I see an error that the table 'datamappers' (or similar) does not exist?

You probably instantiated the DataMapper library. You also might have an object whose table name is not generated correctly. See Why is DataMapper generating the wrong table name? above.

Why isn't DM validating my field?

There are two main reasons for this. Both will easily confuse you at first:

  1. The most common reason for this a field without a 'required' rule. You must include this rule for any field that needs to be non-empty. This is done to prevent PHP errors when validating empty fields. If a field is empty, but not required, then the validation routine skips that field.
  2. Alternatively, the field probably did not change. If the field is not changed between loading an object and saving it, then the field will not be validated. This prevents running the same validation more than once (such as for encrypted fields).

What's the best way to add a method I want to use multiple times?

See Extending DataMapper.

Why can't I use print_r, var_dump, or json_encode on a DataMapper model?

DataMapper models have two specific reasons why these methods will fail to work.

  1. Most of the fields are generated dynamically, including all relationships. This mean that these items will not exist until the first time they are called.
  2. Because the relationships can end up recursive (the first item in the all array is also the containing object), you can sometimes cause print_r to get into an infinite loop.

Instead of these methods, you might want to look at specific fields, one-at-a-time. Also, in the future, I might include a debugging extension.

To replace the json_encode method, please take a look at the json and array extensions.

How can I post-process fields after they are loaded?
How can I pre-process fields before they are saved?

You can post-process fields after a get using Get Rules.

You can pre-process fields before a save using Validation Rules. See Handling Logins for an example of pre-processing.

Why am I getting query errors with select_func?

CodeIgniter has an overly aggressive method for protecting identifiers, and it cannot be disabled. This may break any attempt to include functions in the SELECT statement.

However, with a simple adjustment to the _protect_identifiers method of the DB_driver class, you can get it working again.

See the bottom of this page for a fix.

Why is DataMapper not saving a relationship at all?

The most common cause for this is a mistake in one or both sides of your relationship definition. Make sure that the $has_many or $has_one definition on both objects is correct.

A less common cause for this would be that you enabled the Production Cache, but forgot to disable or clear it after making changes.

Why does my relationship work from one object, but fail from the other?

When configuring a relationship, even a self-relationship, you must define both "sides" of the relationship. This means for a parent/child relationship, you have to specify the parent on the child, and you have to specify the child on the parent.

Here is an example for a complex self-relationship [User (as Boss) has many Employees, and Employees have one Boss]:

// In User
$has_one = array(
    // define the relationship to the boss
    'boss' => array(
        'class' => 'user',
        'other_field' => 'employee'
    )
);

$has_many = array(
    // define the relationship to the employees
    'employee' => array(
        'class' => 'user',
        'other_field' => 'boss'
    )
);
Now the employees can be access as $a_boss->employee->get(), and bosses could be accessed as $an_employee->boss->get().

Why isn't Datamapper saving the same object more than once?

Due to the way DataMapper looks at objects, it is incapable of storing a relationship between the same objects more than once, even when using extra join table columns.

The best solution for this is to use a couple of other features that are unique to Datamapper ORM to store the relationship in a dedicated model. A very rudimentary example is provided below, joining Machine and Part, feel free to enhance it:

// ------------------------------
// create this model:
// ------------------------------
class MachinePart extends DataMapper {
    $has_one = array('machine', 'part');
    // etc.
}

// ------------------------------
// create this table:
// ------------------------------
machineparts: id, machine_id, part_id, setting_1, setting_2, ...

// ------------------------------
// Use it like this in a controller:
// ------------------------------
// add a part to a machine
$m = new Machine(); ...
$p = new Part(); ...
$mp = new MachinePart();
$mp->setting_1 = $setting_1_value;
$mp->save(array($m, $p));

// get all parts for the machine $m
$parts = $m->machinepart;
// simple trick to avoid making multiple queries
$parts->join_related('part', '*', FALSE); // join all ('*') parts fields, with no prefix
$parts->get();
foreach($parts as $part) {
    echo($part->name); // from the Parts table
    echo($part->setting_1); // from the MachineParts table
    echo($part->setting_2);
    ...
}

// remove all machine-part relationships
$m->machinepart->get()->delete_all();

// remove a specific machine-part relationship, by id
$mp = new MachinePart();
$mp->get_by_id($mp_id);
$mp->delete(); // also removes relationships

Using this as a base, you can see how to relate, query, and delete a N:M relationship where the objects need to be related multiple times.

Does DataMapper delete related objects automatically?

In short, no. DataMapper automatically deletes the relationship between two objects if you delete one of those objects. However, it does not delete related objects automatically.

If you need to handle this in multiple locations, I recommend creating an extra method on the model (I usually call delete_deep()), and delete the related objects you want removed.

I have implemented cascading deletes in my RDBMS. Is that a problem?

No, as long as you have installed Datamapper v1.8.1 or higher, and have configured Datamapper to not automatically remove relations when deleting an object.

To disable automatic deletion of relations, set the config variable cascade_delete to FALSE. You can do this globally, per model and per relation.

You need to disable this option (set to FALSE) when you use 'ON DELETE CASCADE' in your RDBMS.

How do I retrieve all records that do NOT have a relationship?

Exactly like you would do it when you write your native SQL statement: using a subquery:

// find all books related to user "John"
$sub_books = new Book();
$sub_books->select('id')->where_related_user('name', "John");

// now get all books not related
$book = new Book();
$book->where_not_in_subquery('id', $sub_books)->get();

This will generate the following query, exactly like you would code it by hand:

SELECT *
FROM (`books`)
WHERE books.id NOT IN (SELECT `books_subquery`.`id`
 FROM `books` `books_subquery`
 LEFT OUTER JOIN `books_users` books_users ON `books_subquery`.`id` = `books_users`.`book_id`
 WHERE `books_users`.`book_id` IN (1, 16, 23) )

A more complex example:

// create the parent and child objects
$parent = new Parent();
$child = $parent->child;

// subquery: select all parent_id's of parent related to child with id 1
$child->select('parent_id');
$child->where('child_id', 1);
$child->where_related('parent', 'id', '${parent}.id');

// now retrieve all parents that are NOT related to child with id 1
$parent->where_not_in_subquery('id', $child)->get();