I am needing to know the ID of the page being viewed from within my layout. Actually, it's rendered within my Layout.
site.com/controller/view/id
or
site.com/controller/id <-- shorthand for the same above
I need to get that id number earlier, within my layout.
There is $this->context->action->id but that returns view. I don't see why it isn't possible for Yii to do this. If it doesn't exist, they should add it :)
I really don't want to chop the url, that's a chincy hack, and chincy hacks usually break later down the road for one reason or another.
You can simply get it from $_GET['id'] or with \Yii::$app->request->get('id');.
If you have some logic depending on controller and item id being viewed consider place it into widget, this will make your layout more readable
EDIT:
id is just a parameter name, to get it in layout you can use two approaches noted above or if you want to use context you can get it like this $this->context->actionParams['id']
You can extend your yii\web\View.php and add $id as a public property.[Or simply add public $id; in your yii\web\View.php]
Then in your view file,add
$this->id='watever';
and get the same in your layout as
<?php echo $this->id; ?>
The content is generated before the layout. Either way inside the controller before you call $this->render('view',['model' => $myModelVar, 'someotherparam' => $somevar']);
You would set the \Yii::$app->params array. This will be accessable inside the layout.
e.g. in your controller.
public function actionView($id) {
\Yii::$app->view->params['layoutvars'] = ['id' => $id];
return $this->render('view', ['model' => $model]);
}
inside your layout file
$id = \Yii::$app->view->params['layoutvars']['id'];
Related
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();
I'm trying to return search results to a new controller than where the search action was performed from. Problem is Results is never accessible from CustomSearchResultPage.ss. I've added inline comments for what I think is happening, am I right in my thinking here?
// Customise content with results
$response = $this->customise(array(
'Results' => $results ? $results->getResults() : '',
));
if ($results) {
$response = $response->customise($results);
}
// Use CustomSearchResultPage.ss template
$templates = array('CustomSearchResultPage', 'Page');
// Create a new CustomSearchResultPage page
$page = CustomSearchResultPage::get_one('CustomSearchResultPage');
// Build a controller using the CustomSearchResultPage
$controller = CustomSearchResultPage_Controller::create($page);
// Add the custom data to the newly minted controller
$result = $controller->customise($response);
// Return the controller and tell it how to render
return $result->renderWith($templates);
The page seems to render as expected just the variable is always empty...
Your explanation is a little hard to follow I'm afraid. So I'm answering for what I can ascertain as below:
Performing a search. This requires loading a controller to do as such.
Customising the current controller with the results
RE-customising the current controller with itself.
Setting the template for the current (double customised) controller.
Disregarding all of the above.
Fetching a random page (or an empty record).
Creating a controller for the empty page.
Customising the new controller with the customised controller of the current controller customised with itself.
Returning that page, which shows no results.
You need only stop at step 4 (skip step 3), and return the customisation ($response).
If there is some reason you think you need another controller however (which seems superflous, but who knows), creating and then customising that one (only) before returning it would be better.
Being that you have only used this second controller for rendering a result, the URL will not have changed or anything. The whole thing seems beyond requirements.
A much more simple way to render a result from this action would probably be:
return $this
->customise(['Results' => $results ? $results->getResults() : null])
->renderWith(['CustomSearchResultPage', 'Page']);
(from the top of my head, may need a little refining).
thanks for reading, I know the question might sound fairly common, and well I wont deny the fact that maybe I'm just formulating what i want wrongly.
Lets start by shooting it straight, then specifying.
I have a cakephp app with 2 layouts, one layout renders the whole "public" page, and lets call it admin "admin" layout that will render only actions to authenticated users.
in my admin layout, I am printing an element which is the navigation bar.
what I want to do is, without setting on every single controller, the options to set a variable containing the navigation bar values (yes dynamically filled from a specific model)
I want to be able, to set a variable, which will contain a list of values gotten from a model.
The Model is called "Section", which is a table, that contains a list of "sections" of the application.
this navigation bar, needs to print the values of each section, so I need them to be dynamic, hence, I think (again I might be wrong) i need to set that variable somewhere, to make it available to the element, so when the layout "admin" is rendered, the menu bar, is actually filled with the available values of the sections.
I tried doing a Configure::write on AppController, but no dice, it just allowes me to use a variable on controllers, when what I want to do is, loop through the arrah "sections_for_menu" or whatever we call it, and then print the options avaliable on the menu bar.
so you are a bit more comfortable with the idea, the nav bar is a bootstrap based "navbar" with "dropdowns".
i should be able to
<ul class="mycoolclass">
<?php
foreach($sections as $section) {
echo '<li>" . $section . "</li>';
}
</ul>
and thus, printing each value on a new list with its link, and whatnot.
I have been reading with no luck, and have to admin that I am myself fairly new to cakephp, been using it for no longer than 2 weeks.
Any help reaching a solution to this need, is highly appreciated.
UPDATE:
Hi #nunser, thank you very much for your reply.
indeed I'm using an element
this is my layout "base"
<body>
<?php echo $this->element('admin_navbar'); ?>
<!-- container -->
<div class="container-fluid">
<!-- , array('element' => 'flash') -->
<?php echo $this->Session->flash('flash'); ?>
<?php /*echo $this->fetch('content');*/echo $content_for_layout; ?>
</div>
</body>
I'll try your suggestion and see how it goes
I have a controller "SectionsController" which is in charge of well all Section related actions on the app, what I need is to set in the global variable, a list of sections, so I can print the link inside my navbar!
lets assume the following scenario
$this->set('sections_for_navigation', $this->Section->find('list', array('fields' => array('id', 'name'))));
so then i can access the variable $sections_for_navigation from my element, and render the list of sections.
[19-02-2014 - update] tried it.
based on what #nunser suggested, im actually able to, beforeFilter, setting the value, as if i var_dump it, it actually gives me the array as i expected.
public function beforeFilter(){
$this->loadModel('Section');
$secciones = $this->Section->find(
'list', array(
'fields' => array(
'id',
'name'
)
)
);
$this->set('navigation', $secciones);
Then call
public function beforeFilter() {
parent::beforeFilter();
}
from the controllers, did the trick!, now, do i have to add a beforeFilter to every controller to share the navigation through all the app?, is there a way to avoid having to add that method to every single controller? (not that it bothers me, so far it does what i need,which is actually great)
For those kind of navigation things, I do it in beforeFilter or beforeRender in the AppController.
public function beforeFilter() {
//queries and stuff that gets the array for navigation
$this->set('mainNavigationBar', $navigation)
//remember to pass that variable to the element in the view
}
As of to where, beforeFilter or beforeRender, it depends on what type of rule you want to stablish. For example, if you will have same elements for navigation everywhere, be it that the user has permission or not, or if you might want to alter the navigation variable depending on the action being executed, I'd go with doing it in beforeFilter. That way you can tweak things in navigation if, for example, the user doesn't have permission to access any Section (totally making up the model relation here). Though for that, you might want to keep access to the navigation array in the controller.
Tweaked example
protected $_mainNav = array();
public function beforeFilter() {
//queries and stuff that gets the array for navigation
$this->set('mainNavigationBar', $navigation)
//remember to pass that variable to the element in the view
$_mainNav = $navigation;
}
//other controller
public function randomAction() {
//some reason that makes you modify the navigation
unset($_mainNav[0]); //making that up...
$this->set('mainNavigationBar', $navigation)
}
Now, if you won't change the navigation values (once you've added them dynamically), then go with beforeRender, that way you can check permissions and other stuff before bothering with queries for navigation (example to follow would be the first one).
If values of the navigation change per controller, overwrite the function like
RandomController
public function beforeFilter() {
parent::beforeFilter();
$_mainNav = array('no nav'); //example
}
And that's it. Don't know if you need more detail than that, if so, please explain you problem a little more. Oh, and you mention "to make it available to the element", so I'm guessing you're using an element for the navigation bar. If not, please do.
I'm looking for a clean way to pass variables to partial views. Consider the following example code:
In my controller I do:
$this->view->articles = $arrayWithArticles;
$this->render('articles.phtml');
In my articles.phtml view I do:
foreach($this->articles as $article) {
// show article
$this->render('comments.phtml');
}
In another controller I do:
$this->view->products = $arrayWithProducts;
$this->render('products.phtml');
In my products.phtml view I do:
foreach($this->products as $product) {
// show product
$this->render('comments.phtml');
}
As you can see I use the same (partial) view comments.phtml to display comments about written articles as well as products. The 'comments' I want to display are in $article->comments and $product->reviews. The partial view will need these to display them.
What would be a clean way to pass them to the partial view. I really don't want to do:
$this->comments = $article->comments;
$this->render('comments.phtml');
Because this would potentially become a pain to keep track off (i.e. setting the same view variables in both the controller as in the view).
Is there a clean solution to pass variables to partial views?
Well, I think adding a parameter to your render() method would be sufficient. Maybe something like...
$this->renderSubView($fileName, $data);
Then in renderSubView() you could do whatever it is that you need to do with the array and return the rendered partial view. This way you don't need to redeclare the variable in view, just pass the data appropriate for that specific partial when it is being rendered.
Hey guys, I am new to CodeIgniter and need some help. I have a controller that formats the content area of a post. The problem is that I also need to create a sidebar that contains dynamic groups, and a right column that contains recent posts. This isn't hard, the problem I'm running into is that I want the sidebar, and right column on every page, and I don't want to recode the same bits to get the data in every controller.
What would be the best way to do this without copy/paste?
There are a lot of ways to do this.
1) Templating: This is my preference for most cases (because my templates are complex), I render my view into a variable using something like:
$content = $this->load->view('myview', $page_data, true);
Then I load it into the template parser (fyi you could load it into another view too) like this:
$this->load->library('parser');
$data = array(
'page_title' => 'My Page Title',
'page_content' => $content,
'side_bar' => side_bar(), // function which generates your side bar
'right_col' => right_col() // function which generates your right column
);
$this->parser->parse('my_template', $data);
Then your template is like:
<html>
<head>
<title>{page_title}</title>
</head>
<body>
<div>{side_bar}</div>
<div>{page_content}</div>
<div>{right_col}</div>
</body>
</html>
2) Load another view in your view: (assumes you menu is a view not a controller) Something like this:
<?php $this->load->view('menu_view'); ?>
3) PHP Includes: exactly how you would do it in plain PHP (just include a url which points to a controller which returns a menu), Something like this:
<?php include("/common/sidebar"); ?>
Codeigniter will render that page and then include it.
4) AJAX.. i use this if the content in the "template" content is less important, like banners, suggested related item lists and such.
Use PHP to generate a static HTML page, such as side_bar.html...
Then you can include it on other pages.
You could look into HMVC. It's especially suited for "widget"-type areas like you are talking about.
Essentially what you will do is create two full MVC structures - one for your sidebar and right column, including a controller, a model(if required), and a partial view. Then, you can call this controller directly from the main view to pull the required content in to the page.
To actually call it from within a view, just place the following in the markup wherever you want the sidebar to appear:
<?php echo modules::run('module/sidebar/index'); ?>
The index isn't required, but I put it there to demonstrate that you can call different methods using modules::run(). You can also pass an unlimited number of parameters to modules::run().
In code igniter, there is an optional third parameter to $this->load->view that lets you return a rendered view as a string, which can in turn be used for assignment. What you can do is create a master template, that has all the common parts, as a very simplified example:
<html>
<head>
</head>
<body>
<?php echo $sidebar; ?>
<?php echo $content; ?>
<?php echo $right_column; ?>
</body>
</html>
Then you can create a private function in your controller to populate the dynamic content of your common parts, and combine them with your content and master template:
private function BuildTemplate($view, $data) {
// Generate sidebar content
$sidebar_data['...'] = 'blah blah';
$master_data['sidebar'] = $this->load->view('sidebar', $sidebar_data, true);
// Generate right column data
$right_data['...'] = 'blah blah';
$master_data['right_column'] = $this->load->view('right_column', $right_data, true);
// Now load your content
$master_data['content'] = $this->load->view($view, $data, true);
// Merge it into the master template and return it
return $this->load->view('master' $master_data, true);
}
Then in your appropriate controller method:
public function index() {
$data['...'] = 'blah';
echo $this->BuildTemplate('index', $data);
}
Which will pull everything together for you. You can optionally add extra arguments to BuildTemplate if you want to add things like page specific titles or scripts.
I'm not sure if your problem is in the view, or in the (dynamic) data to be shown in the (common parts of) that view.
If it's the later (as seems to suggest the phrase 'I don't want to recode the same bits to get the data in every controller'), then you have several options. For example.
Put the logic to get the 'common' data in some function outside the controller, as a helper or inside some model, and call it from your controllers.
Make your controllers inherit your own custom controller, that implements that data gathering function.
Refactor your two controllers into a single controller, with different functions for each scenario.
1-Create a custom library class in library folder with the below code
if (!defined('BASEPATH')) exit('No direct script access allowed');
class LoadView{
function __construct(){
$this->CI =& get_instance();
}
function load_view($page){
$this->CI->load->view('header');
$this->CI->load->view('sidebar');
$this->CI->load->view($page);
$this->CI->load->view('footer');
}
}
2-Now load this library in your controller like this
$this->load->library('loadview');
3-Now call the library method and simply insert your page name and you don't have to include header,sidebar and footer again and again as they will be dynamically included by your library.
$this->loadview->load_view('about.php');