i got some stuck when accessing a yii's web application. I have configured as the same as the owner's setting, but while i tried to access, i got an error "Column must be either a string or an array". How could i solve it? Thanks in advance..
When reporting error messages, it helps to have the precise error message. The actual error message is: "Column name must be either a string or an array". With an exact string you can search the framework files to find where it is mentioned.
Looks like some method somewhere is passing an invalid column name to createInCondition method of CDbCommandBuilder.
See line 722: https://github.com/yiisoft/yii/blob/1.1.13/framework/db/schema/CDbCommandBuilder.php
Looking at a couple instances where that method is called, I would guess that you have a database table without a primary key somewhere. That is one possible explanation for the problem. Other explanations will require a lot more details on your part.
Provide the stack trace that the error page provides you with when in debug mode along with your table schema.
This happens when you don't have a primary key in your table and you try to do an update. I got this problem because I had a composite primary key in my table. I was being handled well on all operations until I wanted to update a model.
Just add an int primary key, call it 'id' to your table with auto increment. It should do the trick.
Be sure to disable schema caching (if you're using that) before you test this. The change wont take effect until your schema cache expires.
Maybe you do not have primary key in your table. If you use the method $model->save() to save or use method $model->update() ($model is CActiveRecord instance), you will get this error.
Because the method update in CActiveRecord using Primary key to update (Read more here
)
Source Code: framework/db/ar/CActiveRecord.php#1115
if($this->_pk===null)
$this->_pk=$this->getPrimaryKey();
$this->updateByPk($this->getOldPrimaryKey(),$this->getAttributes($attributes));
$this->_pk=$this->getPrimaryKey();
You can use method updateAll() instead of update() or updateByPk()
Take a look this link
http://www.yiiframework.com/forum/index.php/topic/3887-cdbexception-column-name-must-be-either-a-string-or-an-array/
It seems your table doesn't have a primary key or the primary key doesn't well restored which usually caused by corrupt back up file.
If you forgot add return value you will have error you showed. Simple example, your model with such method will return error on PK
...
public function relations()
{
}
...
Your have to add return value.
/**
* #return array
*/
public function relations()
{
return array();
}
If you are not using such methods you should delete them, or add 'default return values'. Otherwise it gives errors the same as it was primary key or other DB issues (because model read invalid data and didn't all things it should).
Related
I have a spelling error in migrations:
Schema::create('business_category',function(Blueprint $table){
$table->integer('business_id')->unsinged();
$table->integer('category_id')->unsinged();
});
Schema::create('business_category',function(Blueprint $table){
$table->foreign('business_id')->references('id')->on('business');
$table->foreign('category_id')->references('id')->on('category');
});
and I run "php artisan migrate"
this error has been shown:
[Illuminate\Database\QueryException]
SQLSTATE[HY000]: General error: 1005 Can't create table
'brandecide.#sql-42 4_aa' (errno: 150) (SQL: alter table
business_category add constraint bus
iness_category_business_id_foreign foreign key (business_id)
references business (id))
this error caused by:
$table->integer('business_id')->**unsinged**();
and I should change this to
$table->integer('business_id')->**unsigned**();
to fix it.
How should I understand this from the error?
How should I understand this from the error?
You can't, unfortunately. I agree the error is quite misleading. The problem is that unique() is not a real method. (If so you'd get a method undefined exception)
Instead the call to unique() ends up being caught by Illuminate\Support\Fluent
public function __call($method, $parameters)
{
$this->attributes[$method] = count($parameters) > 0 ? $parameters[0] : true;
return $this;
}
And is then added to $this->attributes without doing any checking. This then results in no error but just an attribute (unsinged) that will never be used and a missing unsigned that causes the constraint to fail.
If you want you can create an issue (with type "proposal") on github. Maybe someone has a good idea how this can be prevented. (e.g. a whitelist of recognized methods)
You probably already have some entries in your business_category table. When running that migration, you're trying to add a column that references another table. As you didn't define any default value, MySQL tries to default the value to null, but that corresponds to nothing in that other table. When having a foreign key, the value in that column MUST correspond to a value in another table.
So to solve it, you could create a ->default(1), assuming that the id 1 exists in your business-table.
Lets say I have a table 'account' with below structure
CREATE TABLE IF NOT EXISTS `account` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Id',
`name` varchar(255) NOT NULL COMMENT 'Name',
`index` int(11) NOT NULL COMMENT 'Index',
`user_id` int(11) NOT NULL COMMENT 'UserID',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`)
)
I have created a model 'Account' for that using gii.
When I try create new account, I want user_id to be assigned from session
So, In the controller, I tried like below.
$model = new Account();
$model->**setUser_Id**(Yii::$app->user->getID());
if ($model->load(Yii::$app->request->post()) && $model->save())
But, I don't write any setUser_Id method in Account model, since I assumed when I call set methods, it will automatically assign the property of ActiveRecord.
But its throwing error. But it works fine when I try
$model->user_id = Yii::$app->user->getID();
As per documentation http://www.yiiframework.com/wiki/167/understanding-virtual-attributes-and-get-set-methods/,
Note that the get/set functions can be called directly as functions (but requiring the usual function-call parentheses):
$x = $model->fullname;
$x = $model->getFullname(); // same thing
$model->active = 1;
$model->setActive(1); // same thing
Can you experts throw some light on this please?
It is correct that it throws an exeception, because you have not written any setter or getter. ActiveRecord in yii implementation uses virtual attributes (implemented via __get()/__set() magic function), which can be accessed like $model->attribute and $model->attribute = $value.
You can not use $model->getAttribute() or $model->setAttribute($value), until you implement them yourself.
[edit] read the question a bit better and did few clarifications to make the answer more comprehensive.
The exception is actually expected.
The way the whole virtual attributes functionality has been implemented in Yii shows that when calling $model->attribute the magic methods __get() and __set() search for the attribute name in the table columns, and if not, search for the getter or setter, respectively, that you might have implemented. (That's very approximate but should give you the idea, more information can be seen by looking at the code: https://github.com/yiisoft/yii2/blob/master/framework/db/BaseActiveRecord.php#L237)
This means that the virtual attributes work only one way natively. Otherwise you can always access them via a direct method call using $model->getAttribute('my_attribute'); and $model->setAttribute('my_attribute', $value);
The example you're taking from the link you posted is out of context (apart having been written for Yii 1): in there you get the getter/setter implemented manually and later called in both ways.
The only case where names might not be coherent with what you have in the database is when the table name contains underscores, in which case they'll be removed: e.g. people_manager becomes PeopleManager as class name for the model.
It's not anyway clear what you're trying to achieve, this might help us give you a better direction on how to achieve what you want.
Your problem is because PHP is case-sensitive about user-defined identifiers. When you use $model->user_id then you should call $model->setUser_id and not $model->setUser_Id or $model->setUserId, etc. I mean, only the first letter after set is capital and the rest is as the field name. Let me now if this answer solved your problem.
I'm trying to update a model, I load the model, take all the data from the POST and then save it, easy... But my record was never updating so went to the log and discovered that the update query is adding a weird condition. FYI, MD_ID is my primary key.
So, I load the model, the next line is the SQL produced by Yii:
$model = Ositems::model()->findByPk($id);
SELECT * FROM "MTODETALLADO_INV" "t" WHERE "t"."MD_ID"=249217
If echo the json_encode of the loaded model I get that dictionary in my browser:
echo json_encode($model->getAttributes());
{""MD_BODEGA":"01","MD_PRODUCTO":"0031253","MD_CANTIDAD":"1","MD_PRECIOTOTAL":"1466",,"MD_PORCENTAJEDESCUENTO":"0","MD_IDCABECERA":"97403","MD_ID":"249217","MD_OBSERVACION":null}
At this point everything looks right, now I take the values from post:
$model->attributes = $_POST;
And here if echo the values of the model I get the new values right, now here is the problem: I save the model and this is the SQL Yii runs (I replaced the :yp_ values to make it more readable)
$model->save();
UPDATE "MTODETALLADO_INV" SET
MD_BODEGA"='01'
MD_PRODUCTO"='0020514
MD_CANTIDAD"='10'
MD_PORCENTAJEDESCUENTO"='0
MD_IDCABECERA"=97403
MD_ID"=249218
MD_PRECIOTOTAL"='36210'
MD_OBSERVACION"=''
WHERE "MTODETALLADO_INV"."MD_ID"=1
And there is the problem! WHERE "MTODETALLADO_INV"."MD_ID"=1, Why would it make it 1 if all this time my model id has been 249218 ?
A few considerations:
My model only takes some columns that I need from the actual table, Yii sets the other columns as null and I omitted them in the previous code.
The table is in a foreign db, I use have a custom ActiveRecord which manages the CDbConnection to a database according to the user. (It's a webservice app)
I followed what the function save() did and could finally find the problem was when it tried to get the primary key. I had this method in my model:
public function primaryKey()
{
return array('MS_ID');
}
}
But it had to be:
public function primaryKey()
{
return 'MS_ID';
}
}
Somehow that was causing the problem.
In Doctrine2.0.6, I keep getting an error: "Column VoucherId specified twice".
The models in question are:
Basket
BasketVoucher
Voucher
Basket links to BasketVoucher.
Voucher links to BasketVoucher.
In Voucher and BasketVoucher, there is a field called VoucherId. This is defined in both models and exists with the same name in both DB tables.
The error occurs when saving a new BasketVoucher record:
$basketVoucher = new BasketVoucher;
$basketVoucher->setVoucherId($voucherId);
$basketVoucher->setBasketId($this->getBasket()->getBasketId());
$basketVoucher->setCreatedDate(new DateTime("now"));
$em->persist($basketVoucher);
$em->flush();
I've checked the models and VoucherId is not defined twice. However, it is used in a mapping. Is this why Doctrine thinks that the field is duplicated?
Here's the relevant code - I haven't pasted the models in their entirety as most of the code is get/set.
Basket
/**
* #OneToMany(targetEntity="BasketVoucher", mappedBy="basket")
* #JoinColumn(name="basketId", referencedColumnName="BasketId")
*/
private $basketVouchers;
public function getVouchers()
{
return $this->basketVouchers;
}
BasketVoucher
/**
* #ManyToOne(targetEntity="Basket", inversedBy="basketVouchers")
* #JoinColumn(name="basketId", referencedColumnName="BasketId")
*/
private $basket;
public function getBasket()
{
return $this->basket;
}
/**
* #OneToOne(targetEntity="Voucher", mappedBy="basketVoucher")
* #JoinColumn(name="voucherId", referencedColumnName="VoucherId")
*/
private $voucherEntity;
public function getVoucher()
{
return $this->voucherEntity;
}
Voucher
/**
* #OneToOne(targetEntity="BasketVoucher", inversedBy="voucherEntity")
* #JoinColumn(name="voucherId", referencedColumnName="VoucherId")
*/
private $basketVoucher;
public function getBasketVoucher()
{
return $this->basketVoucher;
}
Any ideas?
EDIT: I've found that the same issue occurs with another model when I save it for the first time. I am setting the primary key manually. The main issue appears to be saving a relationship within an entity.
In this case, I have a field - DraftOrderId - which is used as the primary key on three models. The first model - DraftOrder - has DraftOrderId as a primary key, which is an auto incrementing value. The other two models - DraftOrderDeliveryAddress, and DraftOrderBillingAddress - also use DraftOrderId as a primary key, but it isn't auto incremented.
What's happening is one of the following issues:
If I save the delivery address entity with a draft order id and set it to persist, I get an error: Column DraftOrderId specified twice. Code:
try {
$addressEntity->getDraftOrderId();
} catch (\Doctrine\ORM\EntityNotFoundException $e) {
if ($addressType == "delivery") {
$addressEntity = new Dpp\DraftOrderDeliveryAddress;
} elseif ($addressType == "billing") {
$addressEntity = new Dpp\DraftOrderBillingAddress;
}
$addressEntity->setDraftOrderId($draftOrder->getDraftOrderId());
$em->persist($addressEntity);
}
(It would also help to know if there's a better way of checking if a related entity exists, rather than trapping the exception when trying to get a value.)
If I remove the line that sets the draft order id, I get an error: Entity of type Dpp\DraftOrderDeliveryAddress is missing an assigned ID.
If I keep the line that sets the draft order id but I remove the persist line, and I also keep the lines later on in the code that sets the name and address fields, I don't get an error - but the data is not saved to the database. I am using flush() after setting all the fields - I'm just not using persist(). In the previous examples, I do use persist() - I'm just trying things out to see how this can work.
I can paste more code if it would help.
I think I've fixed it! A couple of findings:
For a primary key that is not an auto-incrementing value, you need to use:
#generatedValue(strategy="IDENTITY")
You also have to explicitly set the mapped entities when creating them for the first time. At first, I was trying to create the address entity directly, but I wasn't setting the mapped entity within the parent model to reference the address entity. (if that makes any sense)
I'm fairly sure it was mostly due to the lack of the IDENTITY keyword, which for some reason was either saying the key wasn't set, or saying it was set twice.
What is the best way in PHP to handle foreign key exceptions on a mysql database? Is there a mysql class that can be used to simplify any code?
Ideally, what I want to do, as an example, is to try to delete a record where it is the foreign key parent to any number of child tables. The foreign key throws the exception, so then I would like to be able to look at each foreign key table and test it, giving meaningful feedback on the tables and number of records causing the exception. This would then be returned as the error so the end user can reference and delete the offending records.
The way I handle this is to set up my database wrapper class to always throw an exception when you encounter a database error. So, for instance, I might have a class called MySQL with the following functions:
public function query($query_string)
{
$this->queryId = mysql_query($query_string,$this->connectionId);
if (! $this->queryId) {
$this->_throwException($query_string);
}
return $this->queryId;
}
private function _throwException($query = null)
{
$msg = mysql_error().". Query was:\n\n".$query.
"\n\nError number: ".mysql_errno();
throw new Exception($msg,mysql_errno());
}
Any time a query fails, a regular PHP exception is thrown. Note that I would throw these from within other places too, like a connect() function or a selectDb() function, depending on whether the operation succeeded or not.
With that set up, you're good to go. Any place you expect that you might need to be handling a database error, do something like the following:
//assume $db has been set up to be an instance of the MySQL class
try {
$db->query("DELETE FROM parent WHERE id=123");
} catch (Exception $e) {
//uh-oh, maybe a foreign key restraint failed?
if ($e->getCode() == 'mysql foreign key error code') {
//yep, it failed. Do some stuff.
}
}
Edit
In response to the poster's comment below, you have some limited information available to you to help diagnose a foreign key issue. The error text created by a failed foreign key restraint and returned by mysql_error() looks something like this:
Cannot delete or update a parent row:
a foreign key constraint fails
(`dbname`.`childtable`, CONSTRAINT `FK_name_1` FOREIGN KEY
(`fieldName`) REFERENCES `parenttable` (`fieldName`));
If your foreign keys are complex enough that you can't be sure what might cause a foreign key error for a given query, then you could probably parse this error text to help figure it out. The command SHOW ENGINE INNODB STATUS returns a more detailed result for the latest foreign key error as well.
Otherwise, you're probably going to have to do some digging yourself. The following query will give you a list of foreign keys on a given table, which you can examine for information:
select * from information_schema.table_constraints
WHERE table_schema=schema() AND table_name='table_name';
Unfortunately, I don't think there's a magic bullet to your solution other than examining the errors and constraints very carefully.
I think the best bet would be for you to do a transaction. That way, the insert will always be valid, or not done at all. That can return an error message that you can work with as well. This will prevent you from having to manually check every table - the db does it for you.