I have made some changes to Mage_Core_Controller_Request_Http but in the file distributed by with magento. This is not the best way, i know, but i have not been able to work out how to override a file in the Controller directory. I can find out how to override files in the controllers directory.
Can anyone tell me how i can override Mage_Core_Controller_Request_Http in my own extension.
thanks
If you don't want to revert to the include path hack, you can also use reflection to set your own request class on the Mage_Core_Model_App model. You can use an observer for controller_front_init_before event for that.
I'll assume you are familiar how to create an event observer, soI'll only add the code for the observer method. If you need additional information please ask.
// Observer method
public function controllerFrontInitBefore(Varien_Event_Observer $observer)
{
$app = Mage::app();
$reflection = new ReflectionClass($app);
$property = $reflection->getProperty('_request');
$property->setAccessible(true);
$myRequest = new Your_Module_Controller_Request_Http();
$myRequest->setOrigRequest($app->getRequest()); // if needed
$property->setValue($app, $myRequest);
// Proof of concept:
// Loggs Your_Module_Controller_Request_Http
Mage::log(get_class(Mage::app()->getRequest()));
}
Create the class Your_Module_Controller_Request_Http and extend the original Mage_Core_Controller_Request_Http.
After that event your request object will be used instead of the original.
This enables you to stay as upgrade safe as possible, because you do not have to copy over the full class from the cor code pool.
Edit: Vinai's solution is the better one.
Because this class is instantiated directly, you will have to use the so-called include path hack to override.
The order of precedence for include paths which affect Varien_Autoload's work is set in app/Mage.php. That order is as follows:
app/code/local/
app/code/community/
app/code/core/
lib/
Therefore, if you copy your file to an analogous path under the local or community codepools, your definition of that class will be used.
Since Magento 1.7 you can use the method Mage::app()->setRequest($request) to replace the request object within an observer for the controller_front_init_before event as suggested by Vinai.
WARNING for Magento Enterprise: The Full Page Cache won't work with this method, as it relies on changes to the request object done before controller_front_init_before. You either need to manually copy all properties from the old request to the new - or replace the request class with benmarks solution.
Related
How can I override a controller which has been overridden in the community codepool? In my case its the Mage_Contacts_IndexController. The controller that overrides this does some stuff in the postAction function and then calls the parent, which is Mage_Contacts_IndexController. My override of Mage_Contacts_IndexController does not work because of that.
Do I have to override the overriding controller in this case?
Thanks!
Usually when you override an already overridden controller, your controller will get executed first only if your module is alphabetically ahead of the module that has already overridden it. For instance if your module name starts with letter "A" and previous module starts with letter "B", in this case yours will be executed first. This is because when magento finds 2 or more modules overriding same class it will go with the one which was fetched first in alphabetical order. You can try that way.
Also check this: overriding a magento block in multiple modules (and how to ignore the other ones)
Hope this helps!!
Codepool canonot create issue in rewrite ,there may be any issues in your module.You need to check this.
Please check this link here you have find you to rewrite controllers class
I'm developing my second Magento module, which should retrieve some data and render a block containing it. Such block would then be used by other pages.
At the moment, I have almost the whole module in place, but, for some reason, the block is not rendered when I call the controller method. I put some debug information, and I could see that the block's __construct() method is called correctly, but then the template doesn't seem to be loaded and the returned page is blank.
Here's the code for the block, which I copied from another module and modified:
class Company_CustomerData_Block_CustomerSummary extends Mage_Core_Block_Template {
const _TEMPLATE = 'customerdata/customersummary.phtml';
public function __construct() {
// This method is called correctly
parent::_construct();
$this->setTemplate(self::_TEMPLATE);
}
}
The file customersummary.phtml is in app/design/frontend/base/default/template/customerdata, which should be the correct place (or, at least, I think it is). Its content is the following:
It works!
Just some plain text. No tags, no code, nothing. I don't mind that it's a static text, it will be populated with data once complete.
In case of need, here's the code for the Controller (I removed the parts where the data is retrieved, as they don't make a difference):
public function dashboardAction() {
// Customer Data to render in the block
$CustomerData = array(); // Data is retrieved elsewhere
$this->getResponse()->setBody(
$this->getLayout()->createBlock('customerdata/customersummary')
->toHtml()
);
}
What could I be doing wrong? I'm afraid I made some stupid mistake again, but I really can't see it.
Finally two more questions:
How do I pass to the template the data I retrieve in the Controller? Specifically, variable $CustomerData.
Once the block renders, how do I render its content from within a page? The block should return a with some stuff in it, I'd like to render it inside the Customer Dashboard, just below the information that is already there.
Thanks in advance for the help.
Most classes on Magneto derive from Varien_Object. Varien_Object (and it's descendents) claim PHP's constructor (__construct) for themselves, and provide an _construct callback which you can use for whatever you like. What this means is that if you override the native PHP constructor (__construct) you need to remember to do a few things:
Accept the same number of parameters as the base class, and...
Call the parent constructor (parent::__construct) with the parameters your constructor was supplied.
Or, alternatively, use the _construct callback supplied by Varien_Object and you're done. There's no need to remember to call parent::_construct if you're using the Magento callback.
So to fix your code snippet above, you can either change...
public function __construct() {
... to ...
public function _construct() {
This will switch you over the using the Magento callback. Or you can change...
parent::_construct();
.. to ...
parent::__construct();
... to call the parent class' constructor. Remembering to add in the parameters that the parent class supplies.
Alan Storm write a great article about Magento's Block Lifecycle and call back methods recently, which might be of assistance.
To your other questions:
Passing information from the controller to a block is generally done via Magento's registry.
Rendering your template as part of a page requires that you create layout instructions (written in XML) which load your block and place it inside a parent block.
Although Jim's Answer is correct, I'm adding a second part because I found out what else was wrong: symbolic links. As I usually do when I develop plugins for a framework, I use symlinks to avoid copying the files over and over again. However, Magento uses function RealPath(), which resolves the symlink to its full path. As a result, the Template file to be loaded resides in a path outside Magento installation directory, and it can't be loaded for security reasons. This makes perfectly sense, pity it's just not very visible.
To fix the issue, I enabled Allow Symbolic Links in configuration on my Development PC, and now the Template is loaded and rendered correctly.
I think that Magento could do with a Log Viewer in the Admin interface. If there isn't a module that does it already, perhaps I should create one myself.
Thanks everybody for the help.
I know how to override Mage classes (any class within app/code/core)
My question is how to override Varien classes? (classes within lib/Varien)
If I want to override Mage_Adminhtml_Block_Page_Menu
I create a class MyCompany_Adminhtml_Block_Page_Menu under app/code/local/MyCompany/Adminhtml/Block/Page/Menu.php
I name it like:
class MyCompany_Adminhtml_Block_Page_Menu extends Mage_Adminhtml_Block_Page_Menu
Now Magento uses my class MyCompany_Adminhtml_Block_Page_Menu instead of Mage_Adminhtml_Block_Page_Menu
My question is: where should I put the new class, and how to name it, to override, for example Varien_Date (lib/Varien/Date.php)
Thanks
If you must, copy the file and path to the local codepool and make the necessary changes. There is no configuration mapping to change the class name.
Explanation: see the bootstrapping in app/Mage.php. There is a load order set for the codepools and libraries in the following order:
app/code/local/
app/code/community/
app/code/core/
lib/
Typically, Varien_Autoload is responsible for mapping classnames such as Varien_Data_Collection_Db, Mage_Core_Model_Abstract, or Zend_Db_Select to relative filenames (Varien/Data/Collection/Db.php, Mage/Core/Model/Abstract.php, and Zend/Db/Select.php respectively). These file locations are then passed to include(), which internally uses the load order set in the bootstrap. Therefore, if the file Varien/Data/Collection/Db.php is present under one of the "earlier" locations, that version of the class definition will be used.
These type of modifications may be justified, but they should be well-considered and documented, as the entire definition will be owned by you and any upgrades will need to be merged in the future. It would be good to know what you would like to change, as someone else may have a slightly less invasive option.
I am writing my own logging class to save data in a DB. As I looked how CI is doing I noticed there is a log_message() function which handles the logging. There is a load_class function I can't assign to anything in the CI user guide.
1 Why do they put this into an extra function?
2 What/where loads this function files from?
Hope there are some CI guys how can answer :-)
Short answer:
You can write your own log class to override the default CI class:
<?php
// this file is /application/libraries/MY_Log.php
class MY_Log extends CI_Log {
public function write_log($level = 'error', $msg, $php_error = FALSE)
{
// Put your own logging function in here.
// If you want it to still log to a file as usual, use this:
parent::write_log($level, $msg, $php_error);
}
}
Long answer:
The load_class() function is basically a singleton loader. If the class has already been loaded, return a previous instance; otherwise, load it and create the singleton. It is very important in a framework like CI. You have to know that every time you call, say, a database function, it is applying it to the same object, not instantiating a new one (that would get really messy). All CI libraries function this way by default.
An important note: they changed how this functions significantly in version 2.0. Previously, it would only load from the /libraries folder, but now, it will load from /core or wherever you specify when calling the function.
Here's the process for loading, say, the Log class (from your example):
$_log =& load_class('Log');
$_log->write_log($level, $message, $php_error);
This runs the following checks, in sequence:
If the Log class already exists, we're done. Return the singleton.
If not, first check the /system/libraries folder for a "Log.php" file
If no file existed for step #2, now check /application/libraries for a "MY_Log.php" file (or whatever your subclass prefix is set to in your configuration)
If it loaded the default CI class (from the /system folder), but you DO have an extended class under /application, load that class too.
Return a new instance of the class (YOURS if it exists; otherwise, it's the CI_* class)
I've actually never needed to use the load_class() function, as it allows extension fairly seamlessly. However, it's good to know how it works.
So, to override a class, first find where the original resides (usually /system/libraries or /system/core). Put your extending file in the corresponding /application folder (this is important! If it's under /system/core, the extension MUST be under /application/core). Prefix both the filename and the class name with MY_ (or whatever you set in your configuration), and have it extend the CI_ base class.
I am looking to create some new functionality to Magento. I am going to be looking to the url and grabbing a parameter. The issue is, this can be on any page. So I can't just extend a Catalog or Checkout Module.
I thought about extending the session classes but I wasn't sure it really fit. Basically I want to grab a parameter from the url and from there add some functionality if it is set or not. I don't think a class will get auto loaded unless it is instantiated somewhere else with a getModel method, am I wrong?
How can you add a module that doesn't have a url path for controller and what not, but doesn't fit extending one of the core modules?
I looked for a generic event but didn't really see one like before_page_load or something
Take a look at the event controller_action_predispatch in app/code/core/Mage/Core/Controller/Varien/Action.php. This event should get called on each dispatch and allow you to grab whatever parameters you need.
The event passes the controller as data, so you can do this:
function yourEvent( $event ) {
$controller = $event->getController();
// your processing here
}
Let me know if that doesn't fit the bill. Hope that helps!
Thanks,
Joe