Multiple pages with codeigniter? - php

I am trying to find the best way to only have to load my view one time and re-use them on different pages.
For the home page for example I have this code:
function index()
{
/* Load Includes for all pages */
$header = $this->load->view('includes/header','',true);
$scripts = $this->load->view('includes/scripts','',true);
$navigation = $this->load->view('includes/navigation','',true);
$footer = $this->load->view('includes/footer','',true);
/* Load widgets for Home Page*/
$rotator = $this->load->view('widgets/home_feature','',true);
$login = $this->load->view('widgets/login','',true);
$cal = $this->load->view('widgets/calendar_home','',true);
$gallery = $this->load->view('widgets/photos_scroll','',true);
$tags = $this->load->view('widgets/tags_view','',true);
$spotlight = $this->load->view('widgets/spotlight','',true);
$recent = $this->load->view('widgets/activityfeed','',true);
$data=array(
'title'=>'Philly2Night.com',
'MetaDesc'=>'Cities2Night new social network',
'MetaKeywords'=>'Social, Party, Plan, Events',
//Load Includes
'header'=>$header,
'scripts'=>$scripts,
'navigation'=>$navigation,
'footer'=>$footer,
//Load Widgets
'feature'=>$rotator,
'login'=>$login,
'calendar'=>$cal,
'photos'=>$gallery,
'tags'=>$tags,
'spotlight'=>$spotlight,
'recent'=>$recent
);
$this->load->vars($data);
$this->load->view('pages/home_view');
}
How do I create new pages, but referencing these views? I tried making the var global, ideally I want to use switch, and define cases but that alas did not work either.

If I understand correctly, I think you just want to have one common place for all the load code, so that you do not have to copy/paste that part for each action in the controller. If that's the case...
Create a constructor in your controller, move the load->views there, and store them into variables inside the class:
function __construct() {
parent::__construct();
/* Load Includes for all pages */
$this->header = $this->load->view('includes/header','',true);
$this->scripts = $this->load->view('includes/scripts','',true);
$this->navigation = $this->load->view('includes/navigation','',true);
$this->footer = $this->load->view('includes/footer','',true);
}
Be sure to change any $header, $scripts, etc references to $this->header, $this->scripts, etc.
Also, replace __construct with your class name if you're using PHP4.

First of all you shouldn't be loading so many pages. Each file system request takes it's toll on performance. Rather than having "header" and "footer" just combine them into the "home_view" page.
Second, your question sounds like you are stating that once you load the views you want them to remain loaded for each page request afterward. That isn't possible with PHP as each request is a whole new load. However, if you are doing a lot of database queries or calculations and then loading widgets you can cache the widgets for 5 mins or so and save the queries each page. Look in the CodeIgniter forums for cache classes.

This may or may not be of some use:
http://codeigniter.com/forums/viewthread/77279/
I use this on ALL of my CodeIgniter projects, it makes templating very very easy.

Related

Automatic Laravel routing for static pages

I have to maintain a site for a client, they had it made with Laravel, the most frequent requests from them are to create new static pages and the more I create them - the more I feel there are definitely better ways to implement them.
To create a static page I go to the admin panel, a menu "Static Pages", push a button "Create New" and there create an entry that goes into a database table. To make the static page show in the website I have to define it in a controller called "FrontEndController" like this:
public function some_page() {
$page = StaticPages::find(1);
return view('frontend.static_pages.some_page', compact('page'));
}
public function some_other_page() {
$page = StaticPages::find(2);
return view('frontend.static_pages.some_other_page', compact('page'));
}
...
Then define routes like this:
Route::get('some-page', 'FrontEndController#some_page')->name('static_page.some_page');
Route::get('some-other-page', 'FrontEndController#some_other_page')->name('static_page.some_other_page');
Now I always thought that you create an admin panel with a menu "Static Pages" and a button "Create New" so that you don't have to put any code manually, but seemingly the developer had other ideas...
So my question is how do you refactor a code like this so that you don't have to go through all of the repetitive process in any similar scenario and write code manually?
Btw I may need to sometimes send some generic params to different static pages and I want to keep the URLs the same as they are. Please, keep in mind that I'm not a very advanced user of Laravel or even PHP.
There is no need to add extra method and router for per page manually.
I think that is a matter of unexperienced developer ,
So, You need to refactor your codes like this.
Add one router and remove others.
Route::get('/static/{id}','FrontendController#index');
On your Controller.
public function index($id)
{
$page = StaticPages::find($id);
$theme = $page->theme; //assume you have save your html themplate name in your table via theme column
return view('frontend.static_pages.'.$theme, compact('page'));
}
Save url or slug in your database
Route::get('staticpage/{pagename}', 'FrontEndController#MethodName');
$page = StaticPages::where('url',$url)->first();
return view('YourStaticPage', compact('page'));

Setting local variables cleanly in a MVC pattern in PHP

I am experimenting with using the MVC pattern to set local vars in some code ie
$action=basename(__FILE__, '.php'); // load action from filename for consistancy (index for this case)
$controller = new seoController($action . '-seo'); // register controller with page action and parameter
$controller->invoke(); // invoke controller for processing
$page_title = "<insert page title here>";
$page_desc = "<insert page desc here>";
$page_keys = "<insert page keywords here>";
Of course the controller calls the model and does all the backend stuff parsing the input, getting the data and then returning.
What I would like is a clean way to set the local $page_title etc vars from the seoModel that is instantiated in setController without using the $_SESSION or any other hacky kind of way.
Is it ok from a design POV to put methods in the controller to get the info? ie
$page_title = seoController->getPageTitle();
My controllers as of now are not being used in this type of way as all they do is connect my models to the views.
I hope I'm being clear enough with my explanation.
Is it ok from a design POV to put methods in the controller to get the info?
Yes, thats what Controller is meant for.
What I would like is a clean way to set the local $page_title etc vars from the seoModel that is instantiated in setController without using the $_SESSION or any other hacky kind of way.
To avoid using $_SESSION that seems to be a bit overkill for this particular case you can set seoController attributes, for example,
Class seoController
{
$public $page_tile = '';
public method getPageTitle()
{
$model = new seoModel();
$page_title = $model->get_page_title();
$this->page_tile = $page_title;
//you could also return the page title here, skipping that
}
}
And access them from the caller
$controller = new seoController;
$controller->getPageTitle();
$page_title = $controller->page_title;
You would normally have things like meta tags stored with the model it’s describing. So if you’re loading say, a product from a model, then that model may also return the meta tags for that product:
public function show($productId)
{
$product = $this->productModel->findById($productId);
// Meta title may be available at $product->meta_title
return new ViewModel(array(
'product' => $product,
));
}
Your controller action would then return the data needed to be displayed in a view, which could be a HTML template, JSON, XML etc.

Where in Magento is the homepage pulled?

I'm assuming it's one of the app layout files - I want to write a hook in my mobile template to pull a different CMS homepage.
Edit: To clarify, I want to achieve having a different cms page pulled for the hompage of a mobile version of the store vs. the desktop version. Since you can only set one default CMS page in magento admin, seems like there needs to be some custom coding in the mobile template files.
One of the things I love about Magento is the ability to accomplish a lot of things, just by playing with layout files.
I'll refer to Alan Storm's image to illustrate how I accomplished this exact task without having to change code (I hope you don't mind Alan).
As you can see with the image above, the Full Action Name is cms_index_index. You can find this information with debugging tools, like Commerce Bug.
As we have the action name, we can change the layout files to point to a mobile-specific home page. In this method the mobile-specific home page is actually a static block.
Once you have set up your mobile-specific content, you can add the following to your mobile template local.xml file, to use this block for your home page:
<cms_index_index>
<block type="cms/block" name="cms_page"><action method="setBlockId"><block_id>mobile_home</block_id></action></block>
</cms_index_index>
In this case I have set up a mobile_home static block. It will use the same layout name as the desktop home page, but this has already been overridden in the mobile template.
This may not be the best way, but it doesn't involve code changes.
It's probably not as straight forward as you'd like, but here's how this works.
The request for the homepage is routed to the indexAction method of the Mage_Cms_IndexController class.
If you take a look at the indexAction method you can see Magento uses the renderPage method of the cms/page helper object to render the contents of the page
#File: app/code/core/Mage/Cms/controllers/IndexController.php
public function indexAction($coreRoute = null)
{
$pageId = Mage::getStoreConfig(Mage_Cms_Helper_Page::XML_PATH_HOME_PAGE);
if (!Mage::helper('cms/page')->renderPage($this, $pageId)) {
$this->_forward('defaultIndex');
}
}
The $pageId is pulled from Magento's system configuration, and is the URL identifier of the CMS page.
If you hop to the renderPage method
#File: app/code/core/Mage/Cms/Helper/Page.php
public function renderPage(Mage_Core_Controller_Front_Action $action, $pageId = null)
{
return $this->_renderPage($action, $pageId);
}
it wraps the call to the protected _renderPage method. If you hop to THAT method, the page loading code is the following portions.
#File: app/code/core/Mage/Cms/Helper/Page.php
protected function _renderPage(Mage_Core_Controller_Varien_Action $action, $pageId = null, $renderLayout = true)
{
$page = Mage::getSingleton('cms/page');
//...
if (!$page->load($pageId)) {
return false;
}
//...
}
This loads the CMS Page object for the homepage. Notice the model is a singleton, which means other code that instantes the singleton later will have the same page. After this, standard Magento page rendering happens. Possibly relevant to your interests, the content layout blocks end up looking like this
Meaning the block HTML for the CMS page is rendered by the following code in Mage_Cms_Block_Page
#File: app/code/core/Mage/Cms/Helper/Page.php
protected function _toHtml()
{
/* #var $helper Mage_Cms_Helper_Data */
$helper = Mage::helper('cms');
$processor = $helper->getPageTemplateProcessor();
$html = $processor->filter($this->getPage()->getContent());
$html = $this->getMessagesBlock()->toHtml() . $html;
return $html;
}
The getPage method instantiates the same singleton we mentioned above. The other code is what replaces CMS page {{...}} directives with their actual content.
If I was approaching this project, I'd consider a class rewrite for the Mage_Cms_Model_Page object that looks something like this.
public function load($id, $field=null)
{
if( ... is mobile site ... AND ... $id is for the home page ...)
{
$id = ... ID of the mobile site, hard coded or pulled from custom config ...;
}
return parent::load($id, $field);
}
There's also the cms_page_render event which fires after the page has loaded in the _renderPage method. You could try reloading the passed in page object with a different ID in the observer. You could also consider something in the model_load_after or model_load_before events — although that gets trickier to do since you can't directly change the ID.
For code that's not going to leave a single client's system, I usually opt for the rewrite these days, since it's quicker (less expensive for clients) and has less complications (i.e. getting at and changing the information you need) during development. The trade-off is a possible future conflict with someone else who's rewriting the class.
Your milage/philosophy may vary.
Good luck!

Get all "pages" in YII?

I'm trying to create my own xml sitemap. Everything is done except for the part that I thought was going to be the easiest. How do you get a list of all the pages on the site? I have a bunch of views in a /site folder and a few others. Is there a way to explicitly request their URLs or perhaps via the controllers?
I do not want to make use of an extension
You can use reflection to iterate through all methods of all your controllers:
Yii::import('application.controllers.*');
$urls = array();
$directory = Yii::getPathOfAlias('application.controllers');
$iterator = new DirectoryIterator($directory);
foreach ($iterator as $fileinfo)
{
if ($fileinfo->isFile() and $fileinfo->getExtension() == 'php')
{
$className = substr($fileinfo->getFilename(), 0, -4); //strip extension
$class = new ReflectionClass($className);
foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method)
{
$methodName = $method->getName();
//only take methods that begin with 'action', but skip actions() method
if (strpos($methodName, 'action') === 0 and $methodName != 'actions')
{
$controller = lcfirst(substr($className, 0, strrpos($className, 'Controller')));
$action = lcfirst(substr($methodName, 6));
$urls[] = Yii::app()->createAbsoluteUrl("$controller/$action");
}
}
}
}
You need to know what content you want to include in your sitemap.xml, I don't really think you want to include ALL pages in your sitemap.xml, or do you really want to include something like site.com/article/edit/1 ?
That said, you may only want the result from the view action in your controllers. truth is, you need to know what you want to indexed.
Do not think in terms of controllers/actions/views, but rather think of the resources in your system that you want indexed, be them articles, or pages, they are all in your database or stored somehow, so you can list them, and they have a URI that identifies them, getting the URI is a matter of invoking a couple functions.
There are two possiblities -
Case 1:
You are running a static website then you can find all your HTML inside 1 folder - protected/views/site/pages
http://www.yiiframework.com/wiki/22/how-to-display-static-pages-in-yii/
Case 2:
Website is dynamic. Tasks such as generating and regenerating Sitemaps can be classified into background tasks.
Running background taks can be achieved by emulating the browser which is possible in linux using - WGET, GET or lynx commands
Or, You can create a CronController as a CConsoleCommand. How to use Commands in YII is shown in link below -
http://tariffstreet.com/yii/2012/04/implementing-cron-jobs-with-yii-and-cconsolecommand/
Sitemap is an XML which lists your site's URL. But it does more than that.
It helps you visualize the structure of a website , you may have
category
subcategories.
While making a useful extension, above points can be kept into consideration before design.
Frameworks like Wordpress provide way to generate categorical sitemap.
So the metadata for each page is stored from before and using that metadata it discovers and group pages.
Solution by Reflection suggested by #Pavle is good and should be the way to go.
Consider there may be partial views and you may or may not want to list them as separate links.
So how much effort you want to put into creating the extension is subject to some of these as well.
You may either ask user to list down all variables in config fie and go from there which is not bad or you have to group pages and list using some techniques like reflection and parsing pages and looking for regex.
For ex - Based on module names you can group them first and controllers inside a module can form sub-group.
One first approach could be to iterate over the view files, but then you have to take into account that in some cases, views are not page destinations, but page sections included in another pages by using CController::renderPartial() method. By exploring CController's Class Reference I came upon the CController::actions() method.
So, I have not found any Yii way to iterate over all the actions of a CController, but I used php to iterate over all the methods of a SiteController in one of my projects and filter them to these with the prefix 'action', which is my action prefix, here's the sample
class SiteController extends Controller{
public function actionTest(){
echo '<h1>Test Page!</h1></p>';
$methods = get_class_methods(get_class($this));
// The action prefix is strlen('action') = 6
$actionPrefix = 'action';
$reversedActionPrefix = strrev($actionPrefix);
$actionPrefixLength = strlen($actionPrefix);
foreach ($methods as $index=>$methodName){
//Always unset actions(), since it is not a controller action itself and it has the prefix 'action'
if ($methodName==='actions') {
unset($methods[$index]);
continue;
}
$reversedMethod = strrev($methodName);
/* if the last 6 characters of the reversed substring === 'noitca',
* it means that $method Name corresponds to a Controller Action,
* otherwise it is an inherited method and must be unset.
*/
if (substr($reversedMethod, -$actionPrefixLength)!==$reversedActionPrefix){
unset($methods[$index]);
} else $methods[$index] = strrev(str_replace($reversedActionPrefix, '', $reversedMethod,$replace=1));
}
echo 'Actions '.CHtml::listBox('methods', NULL, $methods);
}
...
}
And the output I got was..
I'm sure it can be furtherly refined, but this method should work for any of the controllers you have...
So what you have to do is:
For each Controller: Filter out all the not-action methods of the class, using the above method. You can build an associative array like
array(
'controllerName1'=>array(
'action1_1',
'action1_2'),
'controllerName2'=>array(
'action2_1',
'action2_2'),
);
I would add a static method getAllActions() in my SiteController for this.
get_class_methods, get_class, strrev and strlen are all PHP functions.
Based on your question:
1. How do you get a list of all the pages on the site?
Based on Yii's way of module/controller/action/action_params and your need to construct a sitemap for SEO.
It will be difficult to parse automatically to get all the urls as your action params varies indefinitely. Though you could simply get controller/action easily as constructed by
Pavle Predic. The complexity comes along when you have customized (SERF) URL rules meant for SEO.
The next best solution is to have a database of contents and you know how to get each content via url rules, then a cron console job to create all the urls to be saved as sitemap.xml.
Hope this helps!

Zend bootstrap, including javascript files for certain pages only

I'm loading javascript files in the bootstrap as usual, but there's a file that I want to have included only if it's a page that has a form
->appendFile('http://myurl.com/js/formscript.js');
Is there a way to detect the page being loaded, from the bootstrap so I can decide whether or not to include this file?
I thought about passing a variable from the Form to the view, and then checking for that variable in the bootstrap, but it's not working.
This would be in my form
$layout = new Zend_Layout();
$view = $layout->getView();
$view->formscript = true;
and this would be in my bootstrap
if ($view->formscript)
but var_dump($view->formscript) give me null, so any other ideas to activate js files only in specific conditions?
To include javascript files in a particular pages alone, add the following code in those pages(I mean view scripts - *.phtml).
<?php
$this->headScript()->appendFile('http://myurl.com/js/formscript.js');
?>
Similarly, to add CSS files to a particular page, do the following.
<?php
$this->headLink()->appendStylesheet('http://myurl.com/styles.css');
?>
It is possible, but you do not need your bootstrap. You can just access the variable from your layout:
//form
$view = Zend_Layout::getMvcInstance()->getView();
$view->formscript = TRUE;
//layout
if($this->formscript)
{
$this->headScript()->appendFile('http://myurl.com/js/formscript.js');
}
echo $this->headScript();
Do not use getView() in your form as it will return the view object for the form, not for your application. This has tripped me up more than a couple of times >.>
Your idea to set a flag - something like $view->hasForm - in your view seems like a pretty reasonable approach. But as others have noted, it shouldn't be the form itself that attempts to set the flag since it doesn't really have access to view object until rendering time.
Instead, wherever you place a form into your view - probably in a controller, perhaps even in a front controller plugin - simply set your flag there.
Then your view script or layout can call $this->headScript()->appendFile() if the flag has been set.
Why not move over appendFile() to your form class (of course if you use Zend_Form), you would be sure that your JS line will be created only in the same time as your form. The place for this line is good in init() as well as in render()
class Your_Form extends Zend_Form {
public init(){
$this->getView()->appendFile('http://myurl.com/js/formscript.js');
[...]
}
}

Categories