How do I get current action parameter? - php

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

Related

Laravel How to increment only when its a new insert?

I need to increment 'total_products' field in Categories table every time a new product is added. But I don't want it to increase when an existing product is UPDATED. I'm doing this right now:
Category::findOrNew($product->cat_id)->increment('total_products',1);
But this code doesn't recognize whether its an update or a new insert. It increases 'total_products' no matter its an insert or update. How can I make it increment only when its a new insert?
It is clearly two query, try each with separate query
$cat = Category::find($product->cat_id);
if($cat){
$cat->increment('total_products',1);
}else{
$cat = new Category();
$cat->total_products = 1;
// Inert new category here with all related data
$cat->save();
}
return $cat->id;
Also be sure about when to increase total_products ? after insert or after update.
Use the firstOrCreate method for that:
$category = Category::firstOrCreate(['name' => 'John Doe']);
If you want to know whether the user was created or fetched, check the wasRecentlyCreated property:
if ($category->wasRecentlyCreated) {
// "firstOrCreate" didn't find the user in the DB, so it created it.
} else {
// "firstOrCreate" found the user in the DB and fetched it.
}
You can set total_products field to 0 by default in the migration:
$table->integer('total_products')->default(0);
Then findOrCreate and increment:
Category::query()
->findOrCreate($product->cat_id)
->increment('total_products');

Copy one row from one table to another

I need a little help and I can’t find an answer. I would like to replicate a row from one data table to another. My code is:
public function getClone($id) {
$item = Post::find($id);
$clone = $item->replicate();
unset($clone['name'],$clone['price']);
$data = json_decode($clone, true);
Order::create($data);
$orders = Order::orderBy('price', 'asc')->paginate(5);
return redirect ('/orders')->with('success', 'Success');
}
and i got an error :
"Missing argument 1 for
App\Http\Controllers\OrdersController::getClone()"
.
I have two models: Post and Order. After trying to walk around and write something like this:
public function getClone(Post $id) {
...
}
I got another error
Method replicate does not exist.
Where‘s my mistake? What wrong have i done? Maybe i should use another function? Do i need any additional file or code snippet used for json_decode ?
First of all, make sure your controller gets the $id parameter - you can read more about how routing works in Laravel here: https://laravel.com/docs/5.4/routing
Route::get('getClone/{id}','YourController#getClone');
Then, call the URL that contains the ID, e.g.:
localhost:8000/getClone/5
If you want to create an Order object based on a Post object, the following code will do the trick:
public function getClone($id) {
// find post with given ID
$post = Post::findOrFail($id);
// get all Post attributes
$data = $post->attributesToArray();
// remove name and price attributes
$data = array_except($data, ['name', 'price']);
// create new Order based on Post's data
$order = Order::create($data);
return redirect ('/orders')->with('success', 'Success');
}
By writing
public function getClone(Post $id)
you are telling the script that this function needs a variable $id from class Post, so you can rewrite this code like this :
public function getClone(){
$id = new Post;
}
However, in your case this does not make any sence, because you need and integer, from which you can find the required model.
To make things correct, you should look at your routes, because the url that executes this function is not correct, for example, if you have defined a route like this :
Route::get('getClone/{id}','YourController#getClone');
then the Url you are looking for is something like this :
localhost:8000/getClone/5
So that "5" is the actual ID of the post, and if its correct, then Post::find($id) will return the post and you will be able to replicate it, if not, it will return null and you will not be able to do so.
$item = Post::find($id);
if(!$item){
abort(404)
}
Using this will make a 404 page not found error, meaning that the ID is incorrect.

Yii2 - Bad Request (#400) - Missing required parameters: id

CommentController:
public function actionCreate()
{
$model = new Comment();
var_dump(Yii::$app->request->post());
if ($model->load(Yii::$app->request->post()))
{
$model->user_id = Yii::$app->user->getId();
$model->created_at = time();
$model->updated_at = time();
$model->save();
return $this->redirect(Url::to(['post/view', 'id'=>$model->post_id]));
}
return $this->redirect(Url::to(['/post/index']));
}
If I pass as false to $model->save() the data gets stored in the database with no value in the foreign key field 'post_id' but still doesn't redirect me to the correct page.
New to yii and relatively new to software development, any help would be massively appreciated.
It redirects you to post/view - means PostController - actionView($id), but without param $id, which is required in actionView($id). That's why you get 400 missing param error. You have to add this param - for now if you add false to save() method and it is saved without post_id - you can't redirect user to post/view because you didn't passed id of this object in GET
To avoid such things you should check if $model is really saved by if($model->save()) and if, redirect user to view.
you need to do like below in your controller.If id exist this will keep the id value otherwise it defines and keep it as empty.
public function actionView($id='') {
//
}

Yii deleteAll() records with condition

I've set up a log in process where a verification code is generated, and when successful, is then removed. However, i want to make sure that if there's multiple verification codes for the same user, upon log in success, delete all records for that user.
Here's my code
if ($model->validate() && $model->login()) {
//delete this verification code
$verificationCode->delete();
//delete all existing codes for user_id
VerificationCode::model()->deleteAll('user_id',$user->id);
Yii::app()->user->setReturnUrl(array('/system/admin/'));
$this->redirect(Yii::app()->user->returnUrl);
}
However, this seems to just delete all the records, regardless on different user_id's in table. Can anyone see where I'm going wrong?
If you want to delete record with specified attributes, the cleanest way for this is to use deleteAllByAttributes():
VerificationCode::model()->deleteAllByAttributes(['user_id' => $user->id]);
Seems you call the function delete() in wrong way ... try passing value this way
VerificationCode::model()->deleteAll('user_id = :user_id', array(':user_id' => $user->id));
For Yii2, the documented way is to use the function deleteAll().
I normally pass the arguments as an array, like so:
VerificationCode::deleteAll(['user_id' => $user->id]);
Also, you can use the afterDelete method, to make sure that everytime or everywhere someone deletes one verificationCode, your application will also delete every userVerificationCode. Put this in your verificationCode model class:
protected function afterDelete()
{
parent::afterDelete();
VerificationCode::model()->deleteAll('user_id = :user:id',[':user_id' =>$this->user_id]);
//... any other logic here
}
You can use below method for deleting all user_id entry from database:
$criteria = new CDbCriteria;
// secure way for add a new condition
$criteria->condition = "user_id = :user_id ";
$criteria->params[":user_id"] = $user->id;
// remove user related all entry from database
$model = VerificationCode::model()->deleteAll($criteria);
or you can use another method directly in controller action
VerificationCode::model()->deleteAll("user_id= :user_id", [":user_id"
=>$user->id]);
use below method for redirecting a URL
$this->c()->redirect(Yii::app()->createUrl('/system/admin/'));

Yii on update, detect if a specific AR property has been changed on beforeSave()

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.

Categories