I am building some webapp. I don't want the user to rewrite value in database if it already is there. I am trying to check this with preSave() behavior. Whenever i try to get the value from database i get the value the user just posted in with $_POST[]
public function preSave(\PropelPDO $con = null) {
$obj = new UserQuery();
//let's check if user has set the value
$type = $obj->findOneById($this->getId())->getType();
var_dump($type);// <-- here the value from database is always as user
//have just been selected in form. The value in database
//actually is NULL
//if type in database have been saved then set the current type from database
if(!is_null($type))
$this->setType($type);
return true;
}
So my question is: Why the value from database is never NULL, but some TRUE or FALSE as user have picked in form? It should be NULL cause in that moment save() haven't been executed yet.
I suppose, you're getting the same object because of the Propel instance pooling. It just caches the previously fetched objects. So, you can try to disable instance pooling for the preSave method:
public function preSave(\PropelPDO $con = null) {
\Propel::disableInstancePooling();
// your code...
\Propel::enableInstancePooling();
}
Related
I tried to accomplish a multiple where clause but failed. I want to check if the current Date of the user is equal to created_at and the second clause would be if the user has an entry by user id. I am working on a fitness app where the User can track the km he has run. And rather to create in a database table new entries just add them to the existing entries.
My Question is focused on the problem with the if clause because the variable $hasUserEntries is not equal to null but there is no entry in the database table. It is empty.
I tried instead of using get() I used first(). But the problem is that I wasn't able to use Carbon::today() or it was maybe that I use 3 values in the where clause which I need because I can't get the created_at date only the Date YYYY-MM-DD. At the first() Statement I used a hardcoded DateTime to check with created_at and it worked. But I think I must not explain why hardcode is not optimal.
I searched on Stackoverflow and find that most answers were about using get(). It is fine but why does my else get triggered because from my point of view the database is empty(Null) so the if($hasUserEntries==null)should be triggered.
public function add_km_to_progressbar(Request $request, TrackKM $trackKM)
{
$track_dailies = new TrackDaily();
$track_dailies->user_id = \Auth::user()->id;
$amount_runned_km = $request->input('amount_runned_km');
$amount_runned_km = (int)$amount_runned_km;
$track_dailies->amount = (int)$amount_runned_km;
$track_dailies->type = 1;
$check_requirements = [
'user_id'=>\Auth::user()->id,
'created_at'=>'Carbon::today()'
];
$hasUserEntries = DB::table('track_dailies')
->where('user_id','=',\Auth::user())
->where('created_at','>=',Carbon::today())
->get();
if ($hasUserEntries == null) {
return dd('does not work');
} else {
return dd('does work');
}
}
Expected Result should be the triggering of the if statement because if the database table is empty, the user id does not exist or the date of created_at is not the same as the current date then should be triggered if($hasUserEntries==null). I want to create there a new row if this condition == null in the database.
Actual Result if($hasUserEntries==null) is true even though that the database table is empty. I think that the method get() has values saved that are not related to the database.
I hope that you can help me out.
i think what you should have done is checking to see if the record exist in the database before proceeding...
$checkifuserExist= DB::table('track_dailies')->where('user_id','=',\Auth::user())->where('created_at','>=',Carbon::today())->count();
if($checkifuserExist>0)
{
//proceed to query for fitness
}
else
{
//do something else...
}
with this, it will not throw error!
Try this if case intead:
if (is_empty($hasUserEntries))
$checkifuserExist= DB::table('track_dailies')->where('user_id','=',\Auth::user())->where('created_at','>=',Carbon::today())->count();
if($checkifuserExist>0)
{
//if user really exists
if( $hasUserEntries = DB::table('track_dailies')->where('user_id','=',\Auth::user())->where('created_at','>=',Carbon::today())->first())
{
//update the value of the amount
}
else{
//insert new record
}
}
else
{
// if the user does not exist, do something else...
}
remember theres no way the record for user_id would be null because first there has to be record inside the db
I'm using a yii\filters\PageCache filter for my action and I want to define a cache dependency based on action parameter.
E.g. I want to use yii\caching\DbDependency to select updated_at column of the row with id from the request. How can I reference the id parameter of the action?
Yii::$app->request->get('id');
If you want to add dependency in your cache then try this
$db = Yii::$app->db; // or Category::getDb()
$dep = new DbDependency();
$dep->sql = 'SELECT max(update_at) FROM table';
$model = $db->cache(function ($db) {
return model::find()->asArray()->orderBy('id ASC')->all();
},expirytime, $dep);
The code will check your data in cache if it exist will load from cache otherwise create it and cached it.it will also check your update_at filed if its value is changed then it will update your cache,you don't need to worry about that
and if you want to access current action in yii then try this code
Yii::$app->controller->id; //will return current controller//
Yii::$app->controller->action->id; //will return current action//
Yii::$app->controller->module->id; //will return current module//
i hope this will help you
I am raising a Yii event on beforeSave of the model, which should only be fired if a specific property of the model is changed.
The only way I can think of how to do this at the moment is by creating a new AR object and querying the DB for the old model using the current PK, but this is not very well optimized.
Here's what I have right now (note that my table doesn't have a PK, that's why I query by all attributes, besides the one I am comparing against - hence the unset function):
public function beforeSave()
{
if(!$this->isNewRecord){ // only when a record is modified
$newAttributes = $this->attributes;
unset($newAttributes['level']);
$oldModel = self::model()->findByAttributes($newAttributes);
if($oldModel->level != $this->level)
// Raising event here
}
return parent::beforeSave();
}
Is there a better approach? Maybe storing the old properties in a new local property in afterFind()?
You need to store the old attributes in a local property in the AR class so that you can compare the current attributes to those old ones at any time.
Step 1. Add a new property to the AR class:
// Stores old attributes on afterFind() so we can compare
// against them before/after save
protected $oldAttributes;
Step 2. Override Yii's afterFind() and store the original attributes immediately after they are retrieved.
public function afterFind(){
$this->oldAttributes = $this->attributes;
return parent::afterFind();
}
Step 3. Compare the old and new attributes in beforeSave/afterSave or anywhere else you like inside the AR class. In the example below we are checking if the property called 'level' is changed.
public function beforeSave()
{
if(isset($this->oldAttributes['level']) && $this->level != $this->oldAttributes['level']){
// The attribute is changed. Do something here...
}
return parent::beforeSave();
}
Just in one line
$changedArray = array_diff_assoc($this->attributes,
$this->oldAttributes);
foreach($changedArray as $key => $value){
//What ever you want
//For attribute use $key
//For value use $value
}
In your case you want to use if($key=='level') inside of foreach
Yii 1.1: mod-active-record at yiiframework.com
or Yii Active Record instance with "ifModified then ..." logic and dependencies clearing at gist.github.com
You can store old properties with hidden fields inside update form instead of loading model again.
I am building an application where the user can edit some data and then gets presented with a screen where he can confirm (and comment on) his edits.
In the confirmation form I display the changes that have been made to the entity. This works for "normal" fields. Here is some code that works for checking a single field:
// create $form
// bind $form
if ($form->isValid() {
$data = $form->getData();
// example, get changes of a "normal" field
if ($data['color'] != $entity->getColor()) {
// do something with changes
}
}
But I can't do the same for a relation (example ManyToMany with Users) :
if ($data['users'] != $entity->getUsers()
doesn't work because $data['users'] and $entity->getUsers() refer to the same persistent collection. It is possible to call this function to see if there are changes:
if ($data['users']->isDirty())
but it isn't possible to see what changes were made.
The second problem with the above is that if all items are removed from the persistent collection, Doctrine does not mark it as "changed" (isDirty() = true), so I can't catch the specific change where the user removes all "users" from the entity in the form.
Please note that the code all works, the only problem I have is that I am unable to view/process the changes made on the confirmation step.
Doctrine\ORM\PersistentCollection has internal API (public) methods getSnapshot, getDeleteDiff, getInsertDiff that can be used during lifecycle events of the Doctrine\ORM\UnitOfWork. You could for example check the insert diff of a persistent collection during onFlush.
Solved it like this:
1) To get changes that will be made directly to the Entity, use the following:
// create form
// bind form
// form isValid()
$uow = $em->getUnitOfWork();
$uow->computeChangeSets();
$changeset = $uow->getEntityChangeSet($entity);
print_r($changeset);
2a) To get changes to the relations, use the answer from Lighthart above:
$oldUsers = $entity->getUsers()->toArray();
// bind form
// form isValid
$newUsers = $entity->getUsers()->toArray();
// compare $oldUsers and $newUsers
2b) Use these methods on Persistent Collection to find inserts / deletes:
$newUsers = $entity->getUsers();
$inserted = $newUsers->getDeleteDiff();
$deleted = $newUsers->getInsertDiff();
The only problem with (2b) is that if ALL users are removed and none added then getDeleteDiff() is empty which appears to be a Doctrine bug/idiosyncrasy
Store the original collection in a variable before bind and then compared the new collection after bind. PHP has quite a few array comparison functions, and collections are readily turned into native arrays by $collection->toArray();
eg:
// create form
$oldusers=$entity->getUsers()->toArray();
// bind form
if ($form->isValid() {
$data = $form->getData();
if ($data['users'] != $oldusers) {
// do something with changes
}
}
For Example, i have session 'globalunit', and i want to set that session value use setState function. so i make actionSetUnit and actionGetUnit function in my Controller class to be called in my View class by ajax
//to set globalunit
public function actionSetUnit(){
if (isset($_POST['unit']) && $_POST['unit'] !== null){
Yii::app()->user->setState('globalunit',$_POST['unit']);
echo 'globalunit value now is :'.Yii::app()->user->getState('globalunit');
}
}
//to get globalunit
public function actionGetUnit(){
echo 'globalunit value now is :'.Yii::app()->user->getState('globalunit');
}
i often call actionSetUnit in my view, when i change/click my combo unit in my view. but when i call getUnit function in my view to show globalunit value, the result/respons unstable. sometime the respons is true according the last unit i choose in combo Unit. sometime the respons is false (not change according the value of the last unit i choose in combo unit) :(...
Maybe i too much call setState function to set value session with same name.
any missing in my code??
Make sure only to set the state when the $_POST is filled
//to set globalunit
public function actionSetUnit(){
if (isset($_POST['unit']) && $_POST['unit'] !== null)
Yii::app()->user->setState('globalunit',$_POST['unit']);
}
I guess you are overwriting it sometimes with an empty value. Try to Yii::trace() or Yii::log() your $_POST value to ensure it is filled every call.