Where in Magento is the homepage pulled? - php

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!

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'));

How to visualize pages from further down the url?

This being the worst title imaginable, let me explain my concern as best as I can. So basically in my framework I use URLs of the type site.com/controller/method/parameters
I'll take this page for example site.com/news/edit/12
Then, if I apply no routing, my controller's name has to be News, the called method will be edit with a single parameter 12. Then if, for the sake of demonstrating my concern, I decide to do editing of the news title and body in different pages, I would have to navigate to site.com/news/edit/12/title. Which is where it gets messy and is basically what I want to ask:
What is the proper way of including pages that depend on parameters passed to the controller?
IMHO this looks pretty ugly, and what if I have further more such page separations down the page tree
class News {
public function edit($id, $section){
if(method_exists($this, $section){
return $this->$section($id);
}
}
private function title($id){
// Display page to edit news title
}
private function body($id){
// Display page to edit news body
}
}
Not to mention that this can also cause potential problems with calling existing methods that are not supposed to be called.
Please don't execute methods dynamically based on get params. This can be very dangerous.
One simple way with standard routing:
// site.com/news/edit-title/12
public function editTitle($id);
// or depending on your framework
public function edit_title($id);
Another perhaps more cleaner solution would be to build your custom routes:
// site.com/news/edit/title/12
public function editTitle($id);

Changing layout of view in Joomla 2.5

I know there are several similar topics around but I read and tried most of them but still can't figure out how to do this.
I have a written a component in Joomla 2.5 and it works so far. I have different views and I can load the views using the controller.php.
One of the views shows a table out of my data base (data about teams).
Now I'd like to have another layout of the same view which would display the data base table as a form so can change the content.
That's the file structure:
views/
- teams/
- - tmpl/
- - - default.php
- - - modify.php
- - view.html.php
That's out of the view.html.php file:
...
// Overwriting JView display method
function display($tpl = null) {
...
$this->setLayout('modify');
echo $this->getLayout();
// Display the view
parent::display($tpl);
}
I tried different combinations of setLayout, $tpl = ..., default_modify.php, etc.
but I always either get the default layout or some error like 'can't find layout modify'
I load the site with .../index.php?option=com_test&task=updateTeams
And the controller.php looks like this:
function updateTeams(){
$model = $this->getModel('teams');
$view = $this->getView('teams','html');
$view->setModel($model);
$view->display();
}
I had a similar problem, I created some kind of user profile view and wanted them to be able to edit the fields without having to create a new model for it (would have similar functions, hate redundancy...). What worked for me is to simply call the layout like this:
index.php?option=com_mycomponent&view=myview&layout=edit ("edit" would be "modify" in your case)
To do this I didn't touch the view.html.php (well I did at first but I didn't have to.). And you don't need to use the controller either. If you want to load the modify view, just add a button to your regular view linking to the modify layout. No need to change anything else.
I happen to have written a blog article about it, check it out if you want: http://violetfortytwo.blogspot.de/2012/11/joomla-25-multiple-views-one-model.html
Hope this helps.
Ok this is the problem .. you don't want another layout, you want a new MVC triad that is based on forms rather than rendering. So if you look at any of the core content components you will see in the backend they have a mvc for say ... contacts and one for contact and contact is the editor. If in the front end you will notice that com_content and com_weblinks have mvc for artice/weblink and then separate ones for editing.
You need a really different model and layout and set of actions for editng than for just rendering.
Old topic, but it might still help.
It seems that when one wants to change the layout, the $tpl must not be included in the display() or must be null.
So the previous code would be:
function display($tpl = null) {
/* ... */
$this->setLayout('modify');
// Display the view without the $tpl (or be sure it is null)
parent::display();
}

I need to direct modify the $sf_content what is the best workaround?

Situation: only main page is accessible by default, all other pages needs a logged in user. When a module is loaded without user, a login template should be displayed, and no module. In other words, the $sf_content must be emptied in layout.php which is not 100% ok since there is logic in the layout. Is there elegant way for that? I dont think a helper is OK either....
Check out security filters, this is one standard way security is designed in symfony.
You even can implement your own SecurityFilter class with the functionality you want.
http://symfony.com/legacy/doc/reference/1_4/en/12-Filters#chapter_12_security
It is done by default for you by the sfBasicSecurityFilter filter. You just need a good configuration. Read this part of the Jobeet tutorial. You should use sfDoctrineGuardPlugin (or sfGuardPlugin if you using propell) for user authentication.
To complete my comments above: There are different ways to override the layout. You could use the methods:
setLayout($name)
//or using foward, which forwards current action to a new one (without browser redirection)
forward($module, $action);
inside your action class. In case you wand to modify the layout inside a filter, you can use something simular to this:
class yourFilter extends sfFilter {
public function execute($filterChain) {
if($yourConditionForOverrideTheDefaultLayout) {
//here the syntax to change the layout from the filer
$actionStack = $this->getContext()->getActionStack();
$actionStack->getFirstEntry()->getActionInstance()->setLayout('yourLayout');
}
$filterChain->execute();
}
}
To avoid unnecessary duplication in the layout file you can work with Fragments and Partials.

Correct way to deal with application-wide data needed on every pageview

I am currently involved in the development of a larger webapplication written in PHP and based upon a MVC-framework sharing a wide range of similarities with the Zend Framework in terms of architecture.
When the user has logged in I have a place that is supposed to display the balance of the current users virtual points. This display needs to be on every page across every single controller.
Where do you put code for fetching sidewide modeldata, that isn't controller specific but needs to go in the sitewide layout on every pageview, independently of the current controller? How would the MVC or ZF-heads do this? And how about the rest of you?
I thought about loading the balance when the user logs in and storing it in the session, but as the balance is frequently altered this doesn't seem right - it needs to be checked and updated pretty much on every page load. I also thought about doing it by adding the fetching routine to every controller, but that didn't seem right either as it would result in code-duplication.
Well, you're right, having routines to every controller would be a code-duplication and wouldn't make your code reusable.
Unlike suggested in your question comments, I wouldn't go for a a base controller, since base controllers aren't a good practice (in most cases) and Zend Framework implements Action Helpers in order to to avoid them.
If your partial view is site-wide, why don't you just write your own custom View Helper and fetch the data in your model from your view helper? Then you could call this view helper directly from your layout. In my opinion, fetching data through a model from the view doesn't break the MVC design pattern at all, as long as you don't update/edit these data.
You can add your view helpers in /view/helpers/ or in your library (then you would have to register your view helper path too):
class Zend_View_Helper_Balance extends Zend_View_Helper_Abstract
{
public function balance()
{
$html = '';
if (Zend_Auth::getInstance()->hasIdentity()) {
// pull data from your model
$html .= ...;
}
return $html;
}
}
Note that you view helper could also call a partial view (render(), partial(), partialLoop()) if you need to format your code in a specific way.
This is a pretty simple example, but to me it's enough is your case. If you want to have more control on these data and be able to modify it (or not) depending on a particular view (or controller), then I recommend you to take a look on Placeholders. Zend has a really good example about them here on the online documentation.
More information about custom view helpers here.
When you perform such a task, consider using the Zend_Cache component too, so you won't have to query the database after each request but let's say, every minute (depending on your needs).
What you are looking for is Zend_Registry. This is the component you should use when you think you need some form of global variable. If you need this on EVERY page, then you are best adding it to your bootstrap, if you only need it in certain places add it in init method of relavent controllers.
application/Bootstrap.php
public _initUserBalance()
{
$userId = Zend_Auth::getInstance()->getIdentity()->userId;
$user = UserService::getUser($userId);
Zend_Registry::set('balance', $user->getBalance());
}
application/layouts/default.phtml
echo 'Balance = ' . Zend_Registry::get('balance');
That wee snippet should give you the right idea!
In this case, I usually go with a front controller plugin with a dispatchLoopShutdown() hook that performs the required data access and adds the data to the view/layout. The layout script then renders that data.
More details available on request.
[UPDATE]
Suppose you wanted to display inside your layout the last X news items from your db (or web service or an RSS feed), independent of which controller was requested.
Your front-controller plugin could look something like this in application/plugins/SidebarNews.php:
class My_Plugin_SidebarNews
{
public function dispatchLoopShutdown()
{
$front = Zend_Controller_Front::getInstance();
$view = $front->getParam('bootstrap')->getResource('view');
$view->sidebarNews = $this->getNewsItems();
}
protected function getNewsItems()
{
// Access your datasource (db, web service, RSS feed, etc)
// and return an iterable collection of news items
}
}
Make sure you register your plugin with the front controller, typically in application/configs/application.ini:
resource.frontController.plugins.sidebarNews = "My_Plugin_SidebarNews"
Then in your layout, just render as usual, perhaps in application/layouts/scripts/layout.phtml:
<?php if (isset($this->sidebarNews) && is_array($this->sidebarNews) && count($this->sidebarNews) > 0): ?>
<div id="sidebarNews">
<?php foreach ($this->sidebarNews as $newsItem): ?>
<div class="sidebarNewsItem">
<h3><?= $this->escape($newsItem['headline']) ?></h3>
<p><?= $this->escape($newsItem['blurb']) ?></p>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
See what I mean?

Categories