Yii : clear data form table after delete post - php

I used yii blog from Yii Framework
I want clear data from like table after delete post
I used This code at Post Model
protected function afterDelete()
{
parent::afterDelete();
Like::model()->deleteAll('post_id='.$this->id);
}
But Not Delete data from Like Table after delete post

I suggest you put your code in a beforeDelete, not afterDelete, I think it is better to first delete related data and then the main object.
I also suggest opening CActiveRecord class from YiiFramework and look how exactly are the methods defined, see if you are using the right (public/protected) and if you need to return a "true" or no need to return anything.
Here's a working example of beforeDelete:
protected function beforeDelete() {
if (parent::beforeDelete()) {
// requests
Yii::app()->db->createCommand("DELETE FROM x2_oirequest_city WHERE city_id={$this->id}")->execute();
return true;
}
}

Related

Properly cache a type-hinted model in Laravel

I'm using Redis to cache different parts of my app. My goal is to not make a database query when the user is not logged in, as the app's content don't get updated regularly.
I cache the archive queries in my controller, however when I type hint a model in the controller, the model is retrieved from the database and then passed to the controller:
// My route
Route::get('page/{page:id}', [ PageController::class, 'show' ] );
// My controller
public function show ( Page $page ) {
// Here, the $page will be the actual page model.
// It's already been queried from the database.
}
What I'm trying to do is to try and resolve the page from the cache first, and then if the cache does not contain this item, query the database. If I drop the Page type-hint, I get the desired result ( only the id is passed to controller ) but then I will lose the benefit of IoC, automatic ModelNotFoundException, and more.
I've come across ideas such as binding the page model to a callback and then parsing the request(), but seems like a bad idea.
Is there any way to properly achieve this? I noticed that Laravel eloquent does not have a fetching event, which would be perfect for this purpose.
You can override the default model binding logic:
Models\Page.php
public function resolveRouteBinding($value, $field = null)
{
return \Cache::get(...) ?? $this->findOrFail($value);
}
Read more here https://laravel.com/docs/8.x/routing#customizing-the-resolution-logic
In order to check for existence of the data in Redis, you shouldn't type-hint the model into the controller's action. Do it like this:
public function show($pageId) {
if(/* check if cached */) {
// Read page from cache
} else {
Page::where('id', $pageId)->first();
}
}

How to call function from controller in different view in cakephp 3?

I have function in ProductsController productsCount(). It give me amount of records in table.
public function productsCount() {
$productsAmount = $this->Products->find('all')->count();
$this->set(compact('productsAmount'));
$this->set('_serialize', ['productsAmount']);
}
I want to call this function in view of PageController. I want to simply show number of products in ctp file.
How can i do this?
You can use a view cell. These act as mini controllers that can be called into any view, regardless of controller.
Create src/View/Cell/productsCountCell.php and a template in src/Template/Cell/ProductsCount/display.ctp
In your src/View/Cell/productsCountCell.php
namespace App\View\Cell;
use Cake\View\Cell;
class productsCountCell extends Cell
{
public function display()
{
$this->loadModel('Products');
$productsAmount = $this->Products->find('all')->count();
$this->set(compact('productsAmount'));
$this->set('_serialize', ['productsAmount']);
}
}
In src/Template/Cell/ProductsCount/display.ctp lay it out how you want:
<div class="notification-icon">
There are <?= $productsAmount ?> products.
</div>
Now you can call the cell into any view like so:
$cell = $this->cell('productsCount');
I think it would make more sense to just find the product count in the PageController. So add something like $productsAmount = $this->Page->Products->find('all')->count(); in the view action of PageController, and set $productsAmount. If Page and Products aren't related, then you can keep the find call as is as long as you include a use for Products.
Also check this out for model naming conventions: http://book.cakephp.org/3.0/en/intro/conventions.html#model-and-database-conventions
Model names should be singular, so change Products to Product.
you can not call controller method from view page. you can create helper, which you can call from view page.
here you will get a proper documentation to creating helpers-
http://book.cakephp.org/3.0/en/views/helpers.html#creating-helpers
It just depend on the kind of call you're making because there are 3 cases for your issue..
1- If you're calling by a link to click you simply do:
<?= $this->Html->link(_('Product number'),['controller' =>'ProductsController', 'action' => 'productsCount']) ?>
The 2 other cases are whether you want to render the result straight in that same view, then there are some workaround to do.
1- first you will need to check what are the associations between the Page table and the product table and use BelongTo or hasMany option to bind them togheter for proper use.
2- If no association between the tables then you will nedd TableRegistry::get('Produts'); to pass data from a model to another, just like this way in the Pages controller:
public function initialize()
{
parent::initialize();
$this->Products = TableRegistry::get('Produts');
}
But i quite believe that the first option is more likely what you described.
Also you can define static method as below
public static function productsCount() {
return = $this->Products->find('all')->count();
}
And use self::productsCount() in other action.
This is useful only if you need to get count multiple time in controller. otherwise you can use it directly in action as below:
$this->Products->find('all')->count();

Redirecting (back) to another controller in Yii

I've got a ProjectController and a ClientController.
I'd like to create the option from the project _form.php to have a link next to the client dropdown that redirects to ClientController::actionCreate but passes it some kind of variable to let it know if it is coming from ProjectController::actionUpdate or ::actionCreate.
I was ClientController::actionCreate to do its thing, then, if the user got there via ProjectController::actionCreate OR ::actionUpdate, redirect them back to that page and set the client_id in the project model to match whatever was just created.
If someone is just adding a new client via the regular menu, they can just go with the default redirection (I think it goes to view).
In my _form.php I'm using the following code to link to client::actionCreate
<?php echo " ".Chtml::link('+New client',array('client/create',array('redir'=>'project/'.Yii::app()->controller->action->id)));?>
with the goal of somehow telling the client controller that it needs to send something back to project/update or project/create.
I'd like to use code like this in ClientController::actionCreate
public function actionCreate()
{
$model=new Client;
...
....
if(isset($_POST['Client']))
{
$model->attributes=$_POST['Client'];
if($model->save())
{
if(!empty($model->redir)){
$this->redirect(array($model->redir,'id'=>$model->id));
} else {
$this->redirect(array('view','id'=>$model->id));
}
}
}
...
....
}
I'm very new to Yii, not sure what the best way to accomplish this would be.
If i understood you correctly then You need to distinguish between different calls to same controller.I suppose you are not using YII generated code fro create and update because yii automatically calls the update controller if the call was from update view.I suppose you are using custom update form(not generated by Yii, Yii also generates views through gii). You can do this in these ways.
You can create a hidden field in one of the views in which you want to distinguish. Suppose in update you can write
<?php echo CHtml::hiddenField('name' , 'update'); ?>
this values will be submitted in the form also. and in your controller you can check like this
if(isset($_POST['name']))
{
//do something here
}
Second you can pass it an status in the link like
<?php echo " ".Chtml::link('+New
> client',array('client/create',array('redir'=>'project/'.Yii::app()->controller->action->id,'status'=>'update')));?>
and in your controller you can write as
public function actionCreate($status=null)
{
if($status!=null)
{
//do something here
}
}
If the status was passed to this action then $status will not be null, if was not passed as parameter then it will be null

Detecting changes in the model; php yii framework

I'm creating an audit trail module that i will put in a larger system; and i've created a table to store the trail entries , as an "auditor" what i want to see the currently logged on user, the page where he/she is in, what action he/she did, and what were the changes and when...
these are basically what i want to see; my audit trail table looks like:
User| Timestamp| Module Name| Action| Old Value| New Value| Description
i basically had no problem getting the user, by
Yii::app()->session['username'];
the page/module and action by getting the controller's :
$this->module->getName();
$this->action->id;
My problem lies with the changes old value to new value, the edits done by the user.
i could sort of "sniff" out what edits/ changes he/she did by literally copying the variables and passing it through my function where i create the log.. How do i do this dynamically?
i sort of want to detect if a certain model's properties or attributes has been changed and see what changes were made so that i could get a detail log...Thanks ! sorry, i'm really trying hard to explain this.
In each model that you want to observe you can write a afterFind() method, where you store the current DB attributes into some private variable, e.b. _dbValues. Then in beforeSave() you verify the current attributes with the ones in _dbValues and create an audit record if there was a change.
After you have this working, you can take it a step further and create a behavior from it. You'd put the private variable, the afterFind() and the beforeSave() method there. Then you can attach that behavior to many records.
Quick example:
class Book extends CActiveRecord
{
private $oldAttrs = array();
public static function model($className = __CLASS__)
{
return parent::model($className);
}
public function tableName()
{
return 'book';
}
protected function afterSave()
{
// store history
if (!$this->isNewRecord) {
$newAttrs = $this->getAttributes();
$oldAttrs = $this->getOldAttributes();
// your code
}
return parent::afterSave();
}
protected function afterFind()
{
// Save old values
$this->setOldAttributes($this->getAttributes());
return parent::afterFind();
}
public function getOldAttributes()
{
return $this->oldAttrs;
}
public function setOldAttributes($attrs)
{
$this->oldAttrs = $attrs;
}
}
Your solution is good, but what if there're 2 threads that call ->save() at the same time?
Assume that:
the 1st thread find record, save the A status.
the 2nd thread find record, save the A status.
then 1st thread change record to B, call ->save(). System will log A->B
then 2nd thread change record to C, call ->save(). System will log A->C
summary, there're 2 log: A->B, A->C. If this is not problem for you, just ignore it and do the above solution.

Having an "edit" controller to deal with user editing? Does this design make sense?

Here is the flow:
User creates a text based post.
User edits a text based post (an edit page with the post info is displayed)
User submits the changes to the post (a request sent to the post controller)
Now, if I have MULTIPLE types of posts, I have to check in steps 2 and 3 that the user is indeed updating the RIGHT type of post because someone could very well alter the URL to edit a post of type A when it's really of type B. This leads to a lot of redundant code, such as ...
if(user is indeed the editor && the post type is correct) show the edit page
I think it would make a lot of sense to have an EDIT controller that does all the verification needed in the constructor (or maybe a base class?), and then calls the method. Have you encountered similar issues like this - and if not, does this make any design sense?
CodeIgniter is an MVC. That means that your controllers serve as an intermediate between your models (your data), and your view (front-end). "Edit" is an action that you do to objects, like data. Data objects should be organized within a controller, which calls the actual edit functions from the model.
I'm assuming you have a Post controller. At its core, it should have basic CRUD functions, like adding and editing posts. It should look something like this:
class Post extends CI_Controller
{
function __construct()
{
parent::__construct();
}
function index()
{
// List all posts, perhaps?
}
function add()
{
// Add a post
}
function edit($post_id)
{
// Edit a post
}
function view($post_id)
{
// View a post
}
}
That will give you the following pages:
http://example.com/post
http://example.com/post/add
http://example.com/post/view/1
http://example.com/post/edit/1
Checking for user permissions is its own chapter. If you are using a library like Tank Auth, you can check permissions like so:
if ($this->tank_auth->is_logged_in()) {
// Do stuff
}
That should go at the beginning of each function - or in the __construct(), if you want to DRY it up completely.
Good luck.

Categories