php-activerecord icon indicating copy to clipboard operation
php-activerecord copied to clipboard

validates_uniqueness_of activerecord exception : Base table or view not found

Open isaootika opened this issue 11 years ago • 13 comments

I am trying out php active record, it's a great ORM, however I am at a standstill.

I have looked around google, blogs, phpactiverecord documentation as well as statckoverflow for days but have not been able to come across a suitable solution to this problem.

I am able to carry out the basic CRUD (insert,fetch, modify and delete) operations however as soon as i validate an object property using a static $validates_uniqueness_of filter, i get

Fatal error: Uncaught exception 'ActiveRecord\DatabaseException'

With message

exception 'PDOException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test_ar.models' doesn't exist' in C:\wamp\www\test_AR\AR\lib\Connection.php on line 325

Here is the code i used in full.

set_model_directory('model'); $cfg->set_connections( array( 'development' => 'mysql://root:asdf@localhost/test_ar', 'test' => 'mysql://username:password@localhost/test_database_name', 'production' => 'mysql://username:password@localhost/production_database_name' ) ); }); /* class user extends ActiveRecord\Model { static $validates_presence_of = array(array('username', 'message' => 'Please supply a username')); //this works just fine static $validates_uniqueness_of = array(array('username'));//this line causes the PDO exception } */ $user = new user(); user::create((array('username'=>'mike','password'=>'test','created'=>time()))); $user::create(array('username'=>'mike')); //cannot even reach this line because of the exeption References i have tried/looked at https://github.com/kla/php-activerecord/issues/274 (though i don't really understand what's going on there) http://www.phpactiverecord.org/projects/main/wiki/Validations#validates_uniqueness_of http:// blog.felho.hu/what-is-new-in-php-53-part-2-late-static-binding.html as well as many others. Platform and php version I am using php 5.3.4 and using nightly build (May 8 2013) I have almost failed to get my head around this. Please advise on how to correct this.

isaootika avatar Jun 10 '13 12:06 isaootika

I believe your creation syntax is a bit off, you may want something like this

//create a user in the database but not assigned to a variable
//also this may create an error if you refresh the page as it will have already created a user named mike
User::create((array('username'=>'mike','password'=>'test','created'=>time())));

//create a new user assigned to a variable
//this will have an invalid model as there is already a user called mike
$user = new User(array('username'=>'mike')); 

$user->is_valid(); // this should be false

willpower232 avatar Jun 10 '13 12:06 willpower232

@willpower232 Thanks though changing the creation syntax hasn't really fixed the error. Here is the detailed error. error

isaootika avatar Jun 10 '13 13:06 isaootika

The error implies that it is looking for a table called "models" in your database which is a bit curious. I have added a uniqueness constraint to my site and it is erroring in the exact same way. I daresay this may actually be a bug unless we have both failed in the same way somehow...

Regrettably, I am not a developer of this repo so have not the power of fixing it, I hope someone else is paying attention somewhere...

willpower232 avatar Jun 10 '13 13:06 willpower232

Which verison of php are you using? Or how do you validate uniqueness.

Other wise Then it looks like checking for uniqueness I guess is not common practice. Because similar errors are almost no where to be found on the net.

Perhaps for now we'll will have to use the find('all',array('conditions'=>"username LIKE '%somestring%'") as a dirty work around?

isaootika avatar Jun 10 '13 13:06 isaootika

As a less dirty work around... in your user model you can add this until the bug is sorted out.

public function validate()
{
    if(static::exists(array('conditions'=>"username = ?",$username))
       $this->errors->add('username','must be unique');
}

Edit: It'd probably be useful to check "$this->is_new_record()" in the validation function ;p.

anther avatar Jun 10 '13 14:06 anther

Validating uniqueness actually slipped my mind in this instance which is mighty silly of me :-P

Thanks for the work around, will use that.

The error comes from this line in the validates_uniqueness_of function Validations.php apparently

// Retrieve connection from model for quote_name method
$connection = $this->klass->getMethod('connection')->invoke(null);

willpower232 avatar Jun 10 '13 14:06 willpower232

One more point, the syntax for the exists function does not work for me unless I do this

if (static::exists(array("email" => $this->email)))
    $this->errors->add('email', 'must be unique');

willpower232 avatar Jun 10 '13 14:06 willpower232

Makes sense, I think the proper syntax for my example would've been:

static::exists(array('conditions'=>array("username = ?",$username)));
static::exists(array('conditions'=>array('username'=>$username)));

Glad you have a work around, but this definitely looks to be a proper bug.

anther avatar Jun 10 '13 14:06 anther

One more work around tweak, the work around will return true if you are save() 'ing a model (because you have already created the record) so you need an extra check to make sure that the attributes in question are dirty. For me that looks something like this:

$dirty = $this->dirty_attributes();

if (array_key_exists('email', $dirty) && static::exists(array("email" => $this->email)))
    $this->errors->add('email', 'must be unique');

Edit: argh! editor fail!

willpower232 avatar Jun 10 '13 15:06 willpower232

It is indeed a convenient work around. However checking for dirty attributes will generate a warning if the table is initially empty. (No rows). array_key_exists('email',$dirty) //expects an array. but this wont be the case if the table is empty. (for some reason ;p) so alternatively one could use

 if ($this->is_new_record() && static::exists(array('conditions' => array('email' => $this->email)))) {
        $this->errors->add('username', 'This email is already taken'); //this will work fine even when the table is empty
    }

isaootika avatar Jun 10 '13 18:06 isaootika

For extra bonus points, the dirty attributes array simply contains fields that have been set by your code. Ergo if you have re applied the same value to your model, it will be classed as dirty even though it has not changed. This means the work around gets a little more complicated

$dirty = $this->dirty_attributes();

if (array_key_exists('email', $dirty)) {
    try {
        $currently = User::find($this->id);
    }
    catch (Exception $e) {
        $currently = false;
    }

    if ((
        (is_object($currently) && $currently->email != $this->email)
        || !is_object($currently)
    ) && static::exists(array("email" => $this->email)))
        $this->errors->add('email', 'must be unique');
}

Obviously this will become even more complicated if you validate the uniqueness of multiple fields.

Also note the use of try catch to account for isaootikas most excellent observation.

Edit: missed out a bit in the opening sentence :-p

willpower232 avatar Jun 13 '13 10:06 willpower232

Does anyone have an example of this with the multi column unique? I've put together a stackoverflow for this since its still an issue. I made sure to give this thread credit. http://stackoverflow.com/questions/43705005/phpactiverecord-validates-uniqueness-of-not-working/43705326#43705326

TheDagger avatar Apr 30 '17 11:04 TheDagger

On that same stackoverflow post above I created an answer that you can easily drop in your models as a trait. Let me know if you guys see any errors or improvemnts. It supports multi-column uniques :)

TheDagger avatar May 01 '17 03:05 TheDagger