Invoking cells directly from the controller - cakephp

I am working with cakephp 3.1.7 and figuring out how to invoke view cells or retrieve cell data from the controller. I implemented basic cells with the help of cakephp docs and also http://josediazgonzalez.com/2014/03/20/view-cells/ document which is working fine. However, when I try to return cells directly from the controller I get the following error.
Error: Call to undefined method App\Controller\ProductsController::decorate()
This is what I have:
use Cake\View\Cell;
use Cake\ORM\TableRegistry;
class ProductupdateCell extends Cell
{
public function display($options = []){
if (!empty($options['displaylist'])) {
$this->set('productlist', $options['displaylist']);
return $this;
}else{
$category = $this->request->query['category'];
$this->loadModel('Products');
$query = $this->Products-> find()
-> where(['Products.category' => $category])
-> hydrate(false);
$productlist = $query->toArray();
$this->set('productlist',$productlist);
return $this;
}
}
}
In my controller,
<?php
class ProductsController extends Controller
{
use CellTrait;
public function view($id)
{
$products = $this->Products->findById($id);
$this->set('displaylist', $this->decorate('ProductupdateCell', $products));
}
}
Please correct me where I am going wrong. Is it efficient to use this to update my product list based on user input with ajax request? Can I selectively update the particular cell rendered in my view page? Is there any other method to update the cell directly. Please forgive me if this is a dumb question.

I am working with cakephp 3.1.7 and figuring out how to invoke view cells or retrieve cell data from the controller.
This is an architecturally wrong. They're supposed to be used from the view level.
If you want to have modular and abstracted controller logic either use the CRUD plugin. Or simply go for components. Components are packages of logic that are shared between controllers.
Error: Call to undefined method App\Controller\ProductsController::decorate()
There is no such method in the Controller, CellTrait nor the View class. I don't know from where you got that code, it's also not in the documentation of the cells.

Related

CakePHP 3: Setting a variable in a Listener

Is it possible to set a variable from within a Listener. This is what I am trying accomplish:
I created a Blog plugin and because the Homepage will be complex I wanted to attach an event to beforeRender for the Pages controllers and then return a list of 200 items that can be 'consumed' in different views and elements(ie. /src/Template/Pages/display.ctp and /src/Template/Element/latest_blog_items.ctp)
So far I have created this
// Blog.config/bootstrap.php
<?php
$homepageListener = new HomepageListener();
EventManager::instance()->attach($homepageListener);
And in the same plugin
// Blog.src/Event/HomepageListener.php
<?php
namespace Blog\Event;
use Cake\Event\Event;
use Cake\Event\EventListenerInterface;
use Cake\Core\Configure;
use Cake\ORM\TableRegistry;
class HomepageListener implements EventListenerInterface {
public function implementedEvents() {
return [
'Controller.beforeRender' => 'getBlogItems',
];
}
public function getBlogItems(Event $event) {
if ($event->subject()->name == 'Pages') {
$fBlogItems = TableRegistry::get('Blog.BlogPosts')->find('all')->order(['BlogPosts.created' => 'desc'])->limit(200)->toArray();
// This works, but I didn't want to do it this way
Configure::write('fBlogItems', $fBlogItems);
// My desired solution would be creating a variable similar to what is done in Controllers
// ie. $this->set('fBlogItems', $fBlogItems);
}
}
}
I have not figure out how, if possible, I can set a variable (ie. $this->set('fBlogItems, $fBlogItems) and ensure it is available in my Template files.
Is this even possible or advisable?
Does it break MVC?
Is there a better way?
$event->subject() is your controller instance so just use $event->subject()->set().

How to call a controller method from an action

I'm new in CakepHP and I want to use a method (that returns a value) in an action in CakePHP 3. Sort of like this:
public function specify(){
if(isObject1){
// do something}
}
private isObject1($objname){
return true;
}
What is the right syntax?
CakePHP is PHP
The way to call a method from another method of the same class is the same as with any php project using objects - by using $this:
public function specify() {
$something = 'define this';
if($this->isObject1($something)) {
// do something
}
}
private function isObject1($objname) {
return true;
}
There's more info on how to use objects in The PHP manual.
The answer by #AD7six suggests adding a method within the controller which is not right if it is not going to be used as an action.
I think you should consider creating classes under vendor and including them in your controller and calling your class/method. The convention is vendor/$author/$package. You can either autoload them with composer or use a require call to include your file. If you don't want to create a class and just want to have functions, that can be done too.
Do take a look at cakephp's loading vendor files section.

How to reduce request to my database?

i have a partial view with a gridview in it. so when user clicks add button request will redirect to Add method of unitsController. After add it to database I should refetch all data from database. Is there a way to prevent controller from get all database records?
Below is my current controller
public class UnitsController : Controller
{
TList<Units> model=null;
public ActionResult UnitsPartial()
{
if(model==null)
model = database.GetAll();
return PartialView(model);
}
[HttpPost]
public ActionResult Add(Units unit)
{
if (ModelState.IsValid)
{
database.Save(unit);
model.Add(unit);
}
return PartialView("UnitsPartial", model);
}
In the last line I want to use return PartialView("UnitsPartial", model) instead of return database.GetAll() to prevent a database query. But model is null in Add method.
Is my approach correct or not? And why is model is null in add() method?
--UPDATED
first of all control redirect to UnitsPartial() and will fill model object correctly. after press add button, control will redirect to Add(...) method but this time model is equal to null !!!
what is the problem with it? i tried to pass model.Clone() to partial view
return PartialView("UnitsPartial", model.Clone());
but the result is the same
You can't cache values in the controller class like this using member variables. HTTP is stateless and MVC 3 follows that approach. Each individual call to an action method is going to have a brand new instance of Controller class with the model set to null.
So start by calling database.GetAll() in both action methods and then ask the question "How do I make this more efficient".
I don't know if it is the right approach or not, since I do not fully understand what you try to do, but your model is null because you initialize it as null when the controller is created. you should do a model = new TList() before adding something to it....

Codeigniter Undefined property: xxxx_model::$db only from Model

First the Model class:
class Xxxx_model extends Model
{
function XxxxModel()
{
parent::Model();
$this->load->database();
}
function isInDatabase()
{
// Please ignore the sql query, it's just to show some random sql code with results
11. $result = $this->db->query('SELECT * FROM someTable WHERE ...');
$numberOfRows = $result->num_rows();
...
return $test;
}
}
Now the controller:
function someLogic()
{
$this->load->model('xxxx_Model', 'xxxxModel'); // not necessary to specify
$this->xxxxModel->isInDatabase();
}
When I run this I get the error:
Severity: Notice --> Undefined property: Xxxx_model::$db .../xxxx_model.php line 11
I have no idea why this is. If I put the db code in the controller it seems to work, it's only with this setup in the model that it fails. I can't for the life of me figure out where the code is astray...
You have to load the db library first. In autoload.php add below code,
$autoload[‘libraries’] = array(‘database’);
add library 'datatabase' to autoload.
/application/config/autoload.php
$autoload['libraries'] = array(
'database'
);
Propably you're started new project, like me ;-)
To add to atno's answer:
class Xxxx_model extends Model
{
function XxxxModel() //<--- does not match model name Xxxx_model
{
parent::Model();
$this->load->database();
}
Basically, you are not constructing the class or the parent class Model. If you are on PHP5, you may use __construct(), otherwise you must match the class name exactly, regardless of what alias you load it with in your controller. Example:
class Xxxx_model extends Model
{
function __construct()
{
parent::__construct(); // construct the Model class
}
}
I may be mistaken (haven't used 1.x in a while), but if you construct the Model class, there's no need to load the database if you are using the default connection settings in config/database.php, it should already be loaded for you.
If function XxxxModel() isn't your constructor, you're not loading the database by calling $this->xxxxModel->isInDatabase();
Try autoloading the database library from within autoload.php, or create a proper constructor in your model.

CakePHP: best way to call an action of another controller with array as parameter?

In a controller, what is the most appropriate way to call the action of another controller and also pass an array as parameter?
I know that you can use requestAction to call actions within other controllers. But is it possible to pass arrays as parameters using request action?
And no, I do not want to put the action in the App Controller. So that is not a solution for me.
The only other way I know is to load the other controller as explained in:
http://book.cakephp.org/1.3/en/The-Manual/Developing-with-CakePHP/Configuration.html#importing-controllers-models-components-behaviors-views-and-helpers
But is there an easier way to just call the other controllers action while passing an array as parameter?
I am new to cakePHP so any suggestion is appreciated. Thanks.
I would not advice to use the method requestAction but rather import, and instantiate the needed controller.
CakePHP doc says about requestAction that:
"It is rarely appropriate to use in a
controller or model"
http://book.cakephp.org/view/434/requestAction
Once you imported and loaded the controller you can call any method of this controller with its parameters.
<?php
//Import controller
App::import('Controller', 'Posts');
class CommentsController extends AppController {
//Instantiation
$Posts = new PostsController;
//Load model, components...
$Posts->constructClasses();
function index($passArray = array(1,2,3)) {
//Call a method from PostsController with parameter
$Posts->doSomething($passArray);
}
}
?>
Would it be appropriate for you to move the logic from the second controller into its model, then do something like this in your first controller's action?
$var = ClassRegistry::init('SecondModel')->myMethod($array);
$this->set(compact('var'));
Then, in the view for the first controller's action, you can use that data.
I always try to keep controller methods to actions you can hit through the browser, put as much logic in my models, call foreign model methods from controllers actions that need data from models that aren't the model for that controller, then use that data in my views, and if it's data that is viewed frequently, I create an element for it.
As of CakePHP 1.2.5, you should be able to pass various parameter types through the second parameter in requestAction(). e.g.:
$this->requestAction('/users/view', array('pass' => array('123')));
Then in the UsersController:
function view($id) {
echo $id; // should echo 123 I believe, otherwise try $this->params['pass'].
}
Instead of using 'pass' above, you can alternatively try 'form' and 'named' to pass form/named parameters respectively.
CakePHP 2.X:
<?php
App::uses('AppController', 'Controller');
App::uses('PostsController', 'Controller');
class CommentsController extends AppController {
public function index($parameter = null){
//Instantiate
$Posts = new PostsController();
//Load model, components...
$Posts->constructClasses();
//Call a method of Posts passing a parameter
$Posts->aMethod($parameter);
}
}
I put into my AppController class the following method and variable so it is caches in case of multiple calls
var $controllersArray = array();
function _getController( $pControllerName ){
if ( ! isset($this->controllersArray[$pControllerName]) ){
$importRes = App::import('Controller', $pControllerName);// The same as require('controllers/users_controller.php');
$strToEval = "\$controller = new ".$pControllerName."Controller;";
$evalRes = eval($strToEval);
if ( $evalRes === false ){
throw new AppException("Error during eval of given getController '$pControllerName'");
}
$controller->constructClasses();// If we want the model associations, components, etc to be loaded
$this->controllersArray[$pControllerName] = $controller;
}
$result = $this->controllersArray[$pControllerName];
return $result;
}

Resources