I have discovered Kohana just 2 days ago and, after facing a few problems and managing to deal with them, I ran into a quite strange one.
When I try to update an entry in DB, instead of updating the entry ORM just inserts a new row.
Everything is similar to this example http://kohanaframework.org/3.0/guide/orm/examples/simple
My code (controller file):
class Controller_Admin extends Controller {
...
public function action_test($id)
{
$inquiry = ORM::factory('inquiry')->where('Id', '=', $id)->find(); // $inquiry = Model_Inquiry($id) doesn't work, too
$inquiry ->Name = 'Someone';
$inquiry ->save();
}
...
}
Model file:
class Model_Inquiry extends ORM
{
protected $_table_name = 'mytable';
}
Do you have any ideas why it's inserting a new entry instead of updating the old one? I read that it's because the ID is not correctly set, but I tried using a fixed value (->where('Id', '=', 5)) and it didn't work (it still inserted a new row).
Thanks in advance!
Your record isn't loaded in the first place. To check if it's loaded, do:
if ($inquiry->loaded()){...
Also, you can check what query has been run by doing:
echo $inquiry->last_query();
So you can manually check what exactly is being returned to ORM.
The main problem here is that you're using save() instead of being more strict and using create() / update(). If you used update(), ORM would throw an exception complaining about the record not being loaded.
Save is basically a proxy to these methods, relying on the loaded state.
(I assume that you're using Kohana 3.1 since 3.0 ORM doesn't have separate update / create methods at all)
Related
I've got these two models (changed the names for this example because they aren't in english):
Task:
public function times() {
return $this->hasMany('TaskTime', 'id');
}
TaskTime:
public function task() {
return $this->belongsTo('Task', 'task_id');
}
Also, inside the model Task, I've got this method:
public function start() {
$now = Carbon\Carbon::now();
$time = new TaskTime;
$time->task()->associate($this);
$time->beginning = $now->toDateTimeString();
$time->save();
// testing
echo $this->times()->get()->toJson();
echo '<br/><br/>';
echo $this->toJson();
die();
}
When I call the start() method, it correctly saves a new row in the TaskTime's corresponding table, with the foreign key correctly set to the Task.
The line echo $this->tempos()->get()->toJson(); correctly prints the rows, including the new one.
The line echo $this->toJson(); doesn't print the new row! Only prints the old ones.
I've tried save() and push() in both $this and $time and it still doesn't print the updated data!
Any idea of what can be causing this? I've been trying to debug this thing since yesterday and I ran out of ideas...
The problem is, that Eloquent does not update relation on the already loaded models after attaching, saving, associating etc.
It creates the relation, ie. inserts/updates necessary tables (attach, save, saveMany) or sets the relation on the model without saving anything in db (associate).
So in your case $this has no idea that newly created $tempo has been associated to it.
Now,
`$this->tempos()->get()->toJson();`
runs a new query to fetch related tempos, this is why you get correct result, but
`$this->tempos;
must have been loaded before associating new one, so they won't be reloaded from the db, thus you get 'old' result.
What you need is this:
public function start() {
// do what you need with $time
$tempo->task()->associate($this);
$tempo->save();
$this->load('tempos'); // reloads relation from db
// or:
$this->tempos->add($tempo); // manually add newly created model to the collection
}
Mind thought, that latter solution will cause unexpected result if $this->tempos have not been already loaded.
I have created a migration for ratings, and the table also working when i am entering phpmyadmin.
The problem is, i cannot figure out, how to write to the table?
I am running the code from "story" controller
I am using this:
$z = new Rating();
$z->story_id = 10;
$z->save();
print_r($z);
My "ratings.php" model:
<?php
class Rating extends Eloquent {
protected $table = 'ratings';
}
?>
Is there some place where i should notify laravel that new Rating() means my table "ratings"?
It doesn't seem like i have done the migration correctly, but i am completely new still, so hope someone can figure it out for me.
well instead of using the save() function for laravel you can use the insert() function
Rating->insert_get_id(array('story_id' => '10'));
or
$insert_id = Rating->insert_get_id(array('story_id' => '10'));
for insertion into table.This is much easy to use and I have used this in my whole project and so far I haven't face any problems.
Also if you have not created the model for rating table then go to the models folder under application folder and create a file name rating.php and inside the file write this:
class Rating extends Eloquent
{
public static $timestamps = false;
}
Also please note that table which you created in the phpmyadmin should have name of the form "ratings".
I hope this can be of some help.
I don't really understand what you're doing. Are you trying to write into the table from php? Is Rating a sort of database connection class? You need to create a mysqli object to connect to the database, write a query, and get a result. For best security use a prepared statement. Mysqli Documentation Sorry if I'm off-base about your question, I'm just not positive about what it is.
How can I get the number of affected rows after create, update or delete using ORM for Kohana? I checked the main class but don't return that value...
If there any possible solution to do this, please let me know. If it is possible not use the Query Builder, i prefer use only the ORM class.
Thank you.
Due to its Cascading Filesystem it's very easy in Kohana to make some changes in system or module classes.
In your case you need to redefine methods of ORM module main class. Its file is located in /modules/orm/classes/kohana/orm.php.
Create new ORM module main class file in /application/classes/orm.php and redefine create, update and delete methods so these methods to return the number of affected rows.
For example new update method should end with this:
// Update a single record
$update_result = DB::update($this->_table_name)
->set($data)
->where($this->_primary_key, '=', $id)
->execute($this->_db);
if (isset($data[$this->_primary_key]))
{
// Primary key was changed, reflect it
$this->_primary_key_value = $data[$this->_primary_key];
}
// Object has been saved
$this->_saved = TRUE;
// All changes have been saved
$this->_changed = array();
$this->_original_values = $this->_object;
return $update_result;
Using Symfony 1.4 and doctrine I'd like to save a retrieved model to a different database connection:
retrieve model from master-database
change database connection to slave-database
save the model to the slave-database
I have the 2 connections defined in databases.yml.
here in pseudo-code:
$model = [retrieved from master-database];
$slaveConnection = Doctrine_Manager::getInstance()
->getConnection('slave-connection');
$model->save($slaveConnection);
If I create a new model, $model=new model(); the "code" above successfully saves the model to the slave-connection.
What is going wrong?
According to the Symfony log, Symfony recognizes the model as existing and issues an update (instead of an insert).
UPDATE model SET updated_at = '2011-10-21 17:37:32' WHERE id = '1';
Although Symfony is using the correct database connection ('slave-connection'), the update fails because the model isn't present in the slave-database, yet.
And the insert into the slave-database should use all values of the model, not only the changed ones, too.
Anyone can point me to the right direction to save an existing model to a different database?
edit with my solution.
Thanks samura!
Just some additions:
After performing deep copy Symfony saved a new id. But I wanted to really clone the model object to the slave db and so, I had to modify the id.
That caused unique constraint exceptions, so I had to delete first. So this is it:
$id = $model->getId();
$slaveConnection->execute("delete from modeltable where id=".$id);
$model_copy = $model->copy(true); # deep copy
$model_copy->setId($id);
$model_copy->save($slaveConnection);
hope this helps if someone else stumbles.
You could use the public function copy($deep = false) method of the Doctrine_Record class.
$model = [retrieved from master-database];
$slaveConnection = Doctrine_Manager::getInstance()
->getConnection('slave-connection');
$model_copy = $model->copy(true); # deep copy
$model_copy->save($slaveConnection);
(link to previous question just in case: Struggling with one-to-many relation in an admin form)
I have this many-to-many relation in my Symfony-1.3 / Propel-1.4 project between User and Partner. When the User is being saved, if it has certain boolean flag being true, I want to clear all the links to the partners. Here is what I do at the moment and it doesn't work:
// inside the User model class
public function save(PropelPDO $con = null) {
if ($this->getIsBlaBla()) {
$this->setStringProperty(NULL);
$this->clearUserPartners();
}
parent::save($con);
}
Setting the string property to NULL works; looking at the DB clearly shows it. Thing is however, the USER_PARTNER table still holds the relations between the users and the partners. So I figured I have to clear the links one by one, like this:
foreach($this->getUserPartners() as $user_partner) {
$user_partner->delete();
//UserPartnerPeer::doDelete($user_partner); // tried that too
}
Both don't do the trick.
As I mentioned in my previous question, I am just monkey-learning Symfony via trial and error, so I evidently miss something very obvious. Please point me in the right direction!
EDIT: Here is how I made it work:
Moved the code to the Form class, like so:
public function doSave(PropelPDO $con = null) {
parent::doSave($con);
if ($this->getObject()->getIsSiteOwner()) {
$this->getObject()->setType(NULL);
$this->getObject()->save();
foreach($this->getObject()->getUserPartners() as $user_partner) {
$user_partner->delete();
}
}
return $this->getObject();
}
public function updateObject($values = null) {
$obj = parent::updateObject($values);
if ($obj->getIsSiteOwner()) {
$obj->clearUserPartners();
}
return $this->object;
}
What this does is:
When the boolean flag `is_site_owner` is up, it clear the `type` field and **saves** the object (ashamed I have not figured that out for so long).
Removes all existing UserPartner many-to-many link objects.
Clears newly associated (via the DoubleList) UserPartner relations.
Which is what I need. Thanks to all who participated.
Okey so now you have a many-to-many relation where in database terms is implemented into three tables (User , Parter and UserPartner). Same thing happens on Symfony and Propel, so you need to do something like this on the doSave method that should declare in UserForm:
public function doSave($con = null)
{
parent::doSave($con); //First all that's good and nice from propel
if ($this->getValue('please_errase_my_partners_field'))
{
foreach($this->getObject()->getUserPartners() as $user_partner_relation)
{
$user_partner_relation->delete();
}
}
return $this->getObject();
}
Check the method name "getUserPartners" that should be declared on the BaseUser.class.php (lib/model/om/BaseUser.class.php)
If you are learning Symfony, I suggest you use Doctrine instead of Propel because, I think Doctrine is simplier and more "beautiful" than Propel.
For your problem, I think you are on the good way. If I were you, I will keep my function save() I will write an other function in my model User
public function clearUserPartners(){
// You have to convert this query to Propel query (I'm sorry, but I don't know the right syntax)
"DELETE FROM `USER_PARTNER` WHERE user_id = '$this->id'"
}
With this function, you don't must use a PHP foreach.
But I don't understand what is the attribute StringProperty...
UserPartnerQuery::create()->filterByUser( $userObject )->delete();
or
UserPartnerQuery::create()->filterByUser( $partnerObject )->delete();
Had the same problem. This is a working solution.
The thing is that your second solution, ie. looping over the related objects and calling delete() on them should work. It's the documented way of doing things (see : http://www.symfony-project.org/book/1_0/08-Inside-the-Model-Layer#chapter_08_sub_saving_and_deleting_data).
But instead of bombing the DB with delete queries, you could just as well delete them in one go, by adding a method to your Peer class that performs the deletion using a simple DB query.