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();
Related
In a joomla custom made component there are multiple posts on a page, and every post contains multiple comment, so in view i want to call comments by post id. please suggest a good method to make it working.
You have two options. The first, is to attach the comment id as a URL paramater and retrieve it within the model as needed like so:
$comment_id = JRequest::getApplication()->input->get('comment_id');
If you wish to pass in a parameter when calling the model from the view class, you need to get an instance of the MVC path model instead of using the short cut method. So, instead of using this in the JView class:
$this->items = $this->get('Items');
You would do this instead:
$model = $this->getModel();
$this->items = $model->getItems($comment_id);
Hope this helps.
I am trying to list the grid view of the product variants in product edit page. I have a separate controller and view for variants.
Now I need to know How can I call the variant Controller index method in products edit page, which will return a view with pagination ,search , filter etc.
This is a hard thing to do simple because controllers are HTTP request handlers. So, unless you are making another request, you should not call a a controller method inside your view and it will be hard to do it because they weren't meant to be used this way.
A controller should receive a request, call data processors (repositories, classes), get the result data and send them to the view, get the view result and send it back to the browser. A controller knows very little and does nothing else.
A view should receive data and plot it. There is no problem in being lots and lots of data, but it should receive data (objects are good) and plot them.
If you need to plot a view with pagination pagination, search, filter etc., you don't need a controller call to do it, you can add it as a subview:
#include('products.partials.table')
And you can reuse that view partial in any views. If those tables must be shown only sometimes, you can add conditions to it:
#if ($showTable)
#include('products.partials.table')
#endif
If that partial requires data, you produce that data in your controller:
<?php
class ProductsController extends BaseController {
public function index()
{
$allProducts = $this->productRepository->all();
$filteredProducts = $this->productRepository->filter(Input::all());
$categories = $this->categoriesRepository->all();
return View::make('products.index')
->with('products', compact('allProducts', 'filteredProducts', 'categories'))
}
}
But, still, the less your controller knows about your business, the better, so you could just do:
<?php
class ProductsController extends BaseController {
public function index()
{
$products = $this->dataRepository->getProductsFiltered(Input::only('filter'));
return View::make('products.index')
->with('products', compact('products'))
}
}
And let the repository produce the necessary information you need to plot your data.
I have an action which sets a view variable called "categories" as shown below.
class CategoryController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->categories = 'A list of categories';
}
}
Now the categories variable is used inside the layout to display the list of categories.
But the problem here is this, I have bunch of other actions which are called using front controller plugins and the "categories" variable is overwritten by one of the actions.
Is there a way to prevent the value of categories variable from getting overwritten? I.e. Using something I might be able to store the categories variable generated by the category->index action and other actions and access both in my layout.
I can always change the name of my variables but still would appreciate if there is any other solution to this.
Also, can I achieve this using named response segments?
Thanks for your help.
maybe you add a detect on other action when value is setting, and index action direct setting value.
other action,
if(!isset($this->view->categories))$this->view->categories='xxx';
category->index action
$this->view->categories = 'A list of categories';
I'm trying my best to learn MVC and cakePHP and I had a question about passing arrays to the view. Currently, I have some basic code below.
class AwarenesscampaignsController extends AppController {
public function view($id = null) {
$this->Awarenesscampaign->id = $id;
$this->set('data', $this->Awarenesscampaign->read());
}
This is what I "think" is currently happening.
AwarenesscampaignsController is set up. The view paramater requests id and matches it up with the Model, Awarenesscampaign. This matches up with the database and returns an array which is set to the variable "$data", and then the view is loaded.
My first question: is my understanding accurate?
What I would like to do is with this is to be able to pass another array, from a different model. For instance, I would like to query the table Posts (Controller: PostsController/ Model: Post).
For instance, my first attempt was to do the following inside the function:
$this->Post->find('all');
But this yields the error:
Indirect modification of overloaded property AwarenesscampaignsController::$Post has no effect [APP/Controller/AwarenesscampaignsController.php, line 20]
Additionally, I'm not sure how I would send both variables to the view.
To recap:
Was my understanding accurate?
How do I query a variable from another controller/model?
How do I sent this array to the appropriate view for that controller?
Thanks,
-M
You're on the right lines, and aren't doing it wrong per se. I would say your understanding is pretty good for a beginner.
By default Cake automatically loads a model that it thinks is directly related to the controller. So in AwarenesscampaignController, you can automatically access Awarenesscampaign (the model).
It doesn't know about any other model, though. One way you might solve this is by adding the following property to your controller:
// This has to contain ALL models you intend to use in the controller
public $uses = array('Awarenesscampaign', 'Post');
This goes at the top of the class, before you start declaring the functions. It tells Cake that you want to use other models except the 'default' one, but you have to add that one to the array too, or you'll lose access to it.
You can also use loadModel inside your action, if it's a one-off. It's then accessed the same way as you would access a model normally:
public function view($id = null) {
$this->loadModel('Post');
$posts = $this->Post->find('all');
...
}
To send this to your view, you can call set again, but you might want to change data to something more readable, and to prevent confusion:
public function view($id = null) {
...
$this->set('campaign', $this->Awarenesscampaign->read());
$this->set('posts', $this->Post->find('all'));
}
They'll be accessible as $campaign and $post respectively.
One tweak I would make, though, is to not use 'read' unless you intend to edit something. You can use findByColumnName to get the same data. Since you're using just an id, you can call findById:
$campaign = $this->Awarenesscampaign->findById($id);
There's quite a lot of magic going on there. It just means you can search for a particular value in a more short-hand format.
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html
Finally, while you can access other models (as demonstrated), you can't, or generally shouldn't, try and access one controller from another. If you have code that you want to use in more than one controller, but can't go in the model, you can create Components.
http://book.cakephp.org/2.0/en/controllers/components.html#creating-a-component
The manual is fairly comprehensive. While sometimes hard to navigate, it will often have an answer to most of your questions.
http://book.cakephp.org/2.0/en/
1) Your understanding is good enough. What this is doing is basically mapping a row of database table with object. So after setting the Model id $this->Awarenesscampaign->id = $id, now Model is pointing to the row of database table that has id equals to what has been passed to view action.
2) you can query another table by calling the methods of that particular Model. If your model is somehow associated with the current Model that you are in, you can use chaining to call that Model's action. e.g. if your in Posts controller and Post Model is associated with Comment Model t get the data you can chain through.
$comments = $this->Post->Comment->find();
If however your Model of interest is not associated with current Model, there are couple of ways to perform operations of other Model. A good option is to use Class Registry. Say for example you want to use Customer Model which is not related to your current Model. In your controller you will do
$customer= ClassRegistry::init("Customer");
$customers= $customer->find();
3) to set multiple variables for the view you can set them via compact function or using associated row.
$posts = $this->Post->find();
$comments = $this->Post->Comment->find();
$this->set(compact('posts', 'comments'));
// or
$this->set('posts' => $posts, 'comments' => $comments);
Lets say I have a Company model and Companies controller. Foreign keys in the company table include time_zone_id, station_id, state_id, city_id.
I had actions in the CompaniesController to add, edit, view Companies. For adding and editing, I need dropdown lists of all the foreign key associations (time zones, states, cities, stations). So in those actions, I find myself writing lots of the following for each action:
$this->set('cities', $this->Station->City->find('list'));
$this->set('states', $this->Station->State->find('list'));
etc...
Seems like a lot of code repetition. Is there a better way to go about this?
Add a pre-view callback such as afterFilter to your Companies controller:
function afterFilter() {
// conditional ensures only actions that need the vars will receive them
if (in_array($this->action, array('index', 'view', 'edit'))) {
$this->set('cities', $this->Station->City->find('list'));
$this->set('states', $this->Station->State->find('list'));
}
}
afterFilter will be called after every Companies controller action.
Filters are good, as in webbiedave's answer, but it might get complicated if you end up with a lot of conditional logic.
Another option is that you can define methods in a controller than aren't intended as actions/views: it's a normal PHP object afterall. Better yet, if using PHP5, you can declare these methods private to ensure they cannot be mistaken for actions...
For example, you could define the following in your controller:
private function _populate_dropdowns() {
$this->set('cities', $this->Station->City->find('list'));
$this->set('states', $this->Station->State->find('list'));
}
Then call it at the start of any action that needs it:
public function add() {
$this->_populate_dropdowns();
// ...
// add code
}
public function edit() {
$this->_populate_dropdowns();
// ...
// edit code
}