I have an action that renders two different view scripts based on whether the user is logged in or not.
class IndexController extends Zend_Controller_Action
{
....
public function indexAction()
{
$auth = Zend_Auth::getInstance();
if($auth->hasIdentity())
{
$this->render('indexregistered');
return;
}
else {
$this->render('indexpublic');
return;
}
}
....
}
I have seen quite some useful examples on how to use the Zend Cache and they seem to be based on the fact that the action renders one particular script.
What am really looking at is the best approach to cache the indexpublic script which gets quite some hits and I would really like to avoid the Zend MVC overhead if possible.
Zend_Cache_Frontend_Output may be what you need here:
if (!($cache->start('indexpublic'))) {
// output everything as usual
$this->render('indexpublic');
$cache->end(); // output buffering ends
}
Before that, the cache manager needs to be initialized (could be in the bootstrap), e.g:
$frontendOptions = array(
'lifetime' => 7200
);
$backendOptions = array(
'cache_dir' => '/tmp/'
);
// getting a Zend_Cache_Frontend_Output object
$cache = Zend_Cache::factory('Output',
'File',
$frontendOptions,
$backendOptions);
You're not likely to "avoid the MVC overhead" in any meaningful way here, since that MVC framework is exactly the context which Zend_Cache lives in. Once you're inside a controller action, you've already used a bunch of resources for routing and setup.
That said, if expensive stuff goes on inside of indexpublic.phtml, you might look into using Zend_Cache_Frontend_Output inside your template to cache a bunch of stuff. If indexpublic kicks off expensive operations (like DB hits), this might be worthwhile. If it's just pure PHP that generates markup, you're unlikely to see much improvement.
Before doing anything, I'd suggest you study your application behavior very carefully and make sure you're optimizing in the right place, and not prematurely.
Related
I've volunteered to create some db app, and I told those guys that it will be very easy, since I wanted to use CakePHP. Sadly after some time they told me they want it inside their already existing web, which is ancient highly customized PHPNuke.
So what I want is to generate just content of one <div> inside an already existing page with CakePHP. I looked up on the internet, but I didn't find what I was looking for. I'm rather a user of the framework, not developer, so I don't know much about the backend and how MVC frameworks are working inside (and this is my first try with CakePHP, since I'm Rails guy).
What I did so far is disabling mod_rewrite for Cake. Inside PHPNuke module I included Cake's index.php and rendering views with an empty layout. This somehow works, but the thing is how to form URLs. I got it working by now with
http://localhost/modules.php/posts?op=modload&name=xxxxx&file=index&do=xxxxx
but with this all links to CSS and images on PHPNuke site are broken.
Is there any way to use something like
http://localhost/modules.php?op=modload&name=xxxxx&file=index&do=xxxxx&CakePHP=/posts/bla/bla
or any other way that could do the job? I really don't want to change anything in existing PHPNuke app.
Thank you very much
Well, if you don't understand how CakePHP works you'll have trouble doing what you want, since it would mean putting hacks into the CakePHP core files to bypass the default routing. This basically means that you would be re-working the way CakePHP works, so you can forget about ever updating to a newer CakePHP version, and maintenance would be hell.
If you want to modify the system, but keep PHP-Nuke, I'd advise against jamming CakePHP in there, since that would open up too many problems to be able to predict beforehand.
I think your options are as follows:
Learn how PHP-Nuke works so you can modify it
Use regular php for the pages
Either of those are easier by orders of magnitude compared to what you wanted to do.
So to sum up solution I found, if someone will be looking for something similar. Problem solved by using two custom route classes ( http://manual.cakephp.neoboots.com/2.0/en/development/routing.html#custom-route-classes )
class CustomParserRoute extends CakeRoute {
function parse($url) {
if (parent::parse($url) != false) //if default parser has the match continue
{
// call to Router class to do the routing for new url string again,
// if &cakePHP= is in query string, use this, or use default
if ($_GET['cakePHP']) {
$params = Router::parse($_GET['cakePHP']);
} else {
$params = Router::parse("/my_controller");
}
return $params;
}
return false;
}
}
class CustomMatcherRoute extends CakeRoute {
// cusotm mathc function, that generates url string.
// If this route matches the url array, url string is generated
// with usual way and in the end added to url query used by PHPNuke
function match($url) {
$result_url = parent::match($url);
if($result_url!= false) {
$newurl = function_to_generate_custom_query()."&cakePHP=".$result_url;
return $newurl;
} else {
return $result_url;
}
}
}
And then simple configuration in routes php
App::import('Lib', 'CustomParserRoute');
App::import('Lib', 'CustomMatcherRoute');
// entry point to custom routing, if route starts with modules.php it matches
// the url and CustomParserRoute::parse class is called
// and route from query string is processed
Router::connect('/modules.php', array('controller' => 'my_controller'), array('routeClass' => 'CustomParserRoute'));
// actual routes used by cakephp app, usual routes that need to use
// CustomMatcherRoute classe, so when new url is generated, it is modified
// to be handled later by route defined above.
Router::connect('/my_controller/:action/*', array('controller' => 'my_controller'), array('routeClass' => 'CustomMatcherRoute'));
Let's say you have this scenario:a simple blog home-page that loads both static content as well as dynamic content.
The static content is composed of images that rarely changes.I also have database-driven,dynamic content.The dynamic content consists in all of your blog posts (text and image) and related users comments.The dynamic content changes periodically from every hour to every day.
How would you go with caching?And in particular supposing a user is leaving a comment or the admin is adding/editing a post to you would want to manually trigger the cache clearing to have the update version of this blog home-page?
thanks for your patience.
Luca
thanks again
First, a link: http://framework.zend.com/manual/1.11/en/zend.cache.html
Basically, what you need to do is set up a cache mechanism and then manually call it when you want to retrieve something from the cache.
In the bootstrap, I might have this code:
public function _initCache () {
$cache = Zend_Cache::factory(
'Core',
'File',
array(
'lifetime' => 3600 * 24, //cache is cleaned once a day
'automatic_serialization' => true
),
array('cache_dir' => APPLICATION_PATH.'/cache')
);
Zend_Db_Table_Abstract::setDefaultMetadataCache($cache); //cache database table schemata metadata for faster SQL queries
Zend_Registry::set('Cache', $cache);
}
Then, you may use the load() and save() functions to manipulate the cache. An example from my controller:
$cache = Zend_Registry::get('Cache');
if (!$this->menu = $cache->load('main_menu')) {
$model = new Model_Menu();
$this->menu = $model->get();
$cache->save($this->menu,'main_menu');
}
Here, I check whether the key "main_menu" is cached. If a cache miss is scored, the main menu is generated and cached.
If I edit the main menu, I'll want to regenerate the cache as well. I simply call this:
Zend_Registry::get('Cache')->remove('main_menu');
It's pretty simple, just read the documentation. It's well written.
Zend cache provide a very simple way to store data in cache and to increase the speed. Zend uses frontend and back end to caching. Front end is useful to access or operate the cache. Back end is useful to store data in File , memcache, Sqlite etc.
First of all initialize the fronted and backed in bootstrap file by creating on function in bootstrap file.
protected function _initCache(){
$frontend= array(
'lifetime' => 7200,
'automatic_serialization' => true
);
$backend= array(
'cache_dir' => '../application/tmp/',
);
$cache = Zend_Cache::factory('core',
'File',
$frontend,
$backend
);
Zend_Registry::set('cache',$cache);
}
Then use the zend cache factory to define the cache object. The parameter core define the zend cache core means of generic type File parameter is to define the cache storage means where to store the records of cache then second and forth is for frontend and backend.
Now register that cache array using zend registry so that you can use that are in any controller , model etc.
Define Below code in any controller or any model where you want to use caching of data.
$result1 =””;
$cache = Zend_Registry::get('cache');
if(!$result1 = $cache->load('mydata')) {
echo 'caching the data…..';
$data=array(1,2,3);
$cache->save($data, 'mydata');
} else {
echo 'retrieving cache data…….';
Zend_Debug::dump($result1);
}
First of all in above code we get the cache array. Now if result one is not set then caching done means the file is generated at the path that you define in back-end array
For the Next time page load that data is retrieve from the file where the caching data store.
You can check the file as per defined path.
In that file data is in json format.
So the basic usage of cache is shown by #mingos. He talks about generic cache, which is good. However, ZF has few different cache mechanisms that you can use for different things. You don't need to limit yourself for one type of cache. You can use a mixture of them. For example, for caching your static content Zend_Cache_Frontend_Page would be worth considering as it would generate a full html file of your static pages. If you have lots of config files, e.g. long routes.ini or whatever, you can cache them using Zend_Cache_Frontend_File. With this you would save time parsing the ini files for every request. Significant parts of your views could be cached using Zend_Cache_Frontend_Output, etc.
What to cache and when to update a cache is a tricky question. It all depends on how fast and how often your content is changing. For example, if you have like 100 new comments per second, there is no point in cleaning your comment cache 100 times per second (i.e. for each new comment). It would be better maybe to have comments for each post cached separately from the comments of other posts. Then you would clean/refresh a cache associated with only this post.
A simple cache is one that times out after the defined period of time. This keeps the caching layer simple and easy to implement. The Zend Manual has more information on caching basics.
However real-time information and cached information are two worlds. If you need real-time, don't cache.
If you make the caching layer too complex, you can destroy your whole application.
So, I am looking at a number of ways to store my configuration data. I believe I've narrowed it down to 3 ways:
Just a simple variable
$config = array(
"database" => array(
"host" => "localhost",
"user" => "root",
"pass" => "",
"database" => "test"
)
);
echo $config['database']['host'];
I think that this is just too mutable, where as the configuration options shouldn't be able to be changed.
A Modified Standard Class
class stdDataClass {
// Holds the Data in a Private Array, so it cannot be changed afterwards.
private $data = array();
public function __construct($data)
{
// ......
$this->data = $data;
// .....
}
// Returns the Requested Key
public function __get($key)
{
return $this->data[$key];
}
// Throws an Error as you cannot change the data.
public function __set($key, $value)
{
throw new Exception("Tried to Set Static Variable");
}
}
$config = new stdStaticClass($config_options);
echo $config->database['host'];
Basically, all it does is encapsulates the above array into an object, and makes sure that the object can not be changed.
Or a Static Class
class AppConfig{
public static function getDatabaseInfo()
{
return array(
"host" => "localhost",
"user" => "root",
"pass" => "",
"database" => "test"
);
}
// .. etc ...
}
$config = AppConfig::getDatabaseInfo();
echo $config['host'];
This provides the ultimate immutability, but it also means that I would have to go in and manually edit the class whenever I wanted to change the data.
Which of the above do you think would be best to store configuration options in? Or is there a better way?
Of those 3 options, a static method is probably the best.
Really, though, "the best" is ultimately about what's easiest and most consistent for you to use. If the rest of your app isn't using any OO code then you might as well go with option #1. If you are ultimately wanting to write a whole db abstraction layer, option #2.
Without knowing something more about what your goals are and what the rest of your app looks like, it's kind of like asking someone what the best motor vehicle is -- it's a different answer depending on whether you're looking for a sports car, a cargo truck, or a motorcycle.
I'd go with whats behind door #3.
It looks easier to read and understand than #2, and seems to meet your needs better than #1.
Take a look at this question for ideas on storing the config data in a separate file:
Fastest way to store easily editable config data in PHP?
I'd use method #2 pulling the config data as an array from an external file.
The best way is that which fits your application best.
For a small app, it might be totally sufficient to use an array, even it is mutable. If no one is there to modify it except you, it doesn't have to be immutable.
The second approach is very flexible. It encapsulates data, but does not know anything about it. You can pass it around freely and consuming classes can take from it what they need. It is generic enough to be reused and it does not couple the config class to the concrete application. You could also use an interface with this or similar classes to allow for type hints in your method signatures to indicate a Config is required. Just don't name it stdDataClass, but name it by it's role: Config.
Your third solution is very concrete. It hardcodes a lot of assumptions about what your application requires into the class and it also makes it the responsibility of the class to know and provide this data through getters and setters. Depending on the amount of components requiring configuration, you might end up with a lot of specific getters. Chances are pretty good you will have to rewrite the entire thing for your next app, just because your next app has different components.
I'd go with the second approach. Also, have a look at Zend_Config, as it meets all your requirements already and let's you init the Config object from XML, Ini and plain arrays.
I'm having trouble with the page hierarchy of Zend_Navigation getting scrambled when I cache it using Zend_Cache.
Background: I have approx 50 pages and approx 300 products stored in a database. I query the database and create an array to feed to Zend_Navigation.
The cache is
$secondsBetweenRefreshes = APPLICATION_ENV == 'development' ? 30 : 300;
self::$cache = Zend_Cache::factory( 'Core', 'File',
array( 'automatic_serialization' => true,
'lifetime' => $secondsBetweenRefreshes ),
array( 'cache_dir' => CACHE_PATH . 'nav/' )
);
This works fine
$struct = $cache->load('Zend_Navigation');
if (empty($struct))
{
$cache->save($struct = self::getSiteStructure() );
}
return new Zend_Navigation( $struct );
And this gets scrambled
$struct = $cache->load('Zend_Navigation');
if (empty($struct))
{
$cache->save($struct = new Zend_Navigation( self::getSiteStructure() );
}
return $struct;
The navigation works fine if it is not pulled from the cache. Yes, the obvious solution is not to cache the Zend_Navigation, but the constructor does a lot of work to build its internal representation: makes sense to do the caching with as much pre-computed as I can...
I'm still trying to see if there is a pattern in the scrambled results. There are no loops / cycles in the tree.
After reading this question, I took a quick glance at the Zend_Navigation code and it does not seem that there should be any inherent issues with caching it using serialisation. However looking at the Zend_Navigation documentation I found the following:
The toArray() method converts the container and the pages in it to an array. This can be useful for serializing and debugging.
- Zend Navigation Containers: Other
You may want to create the Zend_Navigation object, use the toArray() function to create the array and cache that. Re-creating the pages from an array should be fairly inexpensive, although you may want to do some testing.
Also, if possible, file a bug report with the Zend Framework maintainers so that they can take a look at it.
Zend_Navigation although an interesting component, and arguably a useful one is not something I use much. For big websites having 10,000+ objects in memory is not a smart idea, and the way certain items are implemented in Zend_Navigation makes it slow and unwieldy. Many developers using the Zend Framework have found other ways of accomplishing the same goals.
Just a warning about caching pages array...
If you cache the array from toArray(), and want to simply use setPages($pagesFromCache), you need to deactive all pages in cached array for future use.
Be careful, Zend_Navigation_Page_Mvc::isActive() checks current request. It means isActive() of your current MVC page still return true, even if you called setActive(false).
The only way I found to deactivate all pages is to recursively walk trough the resulting array :
$pages = $navigation->toArray();
array_walk_recursive(
$pages,
function(&$item, $key)
{
if($key === 'active')
$item = false;
}
);
$cache->save($pages, 'pages');
Thanks for the post; I thought there may be issues combining Zend_Navigation and Zend_Cache, but I've been able to use the save method on a normal Zend_Navigation object and then retrieve the object back from the cache without any need to use toArray().
X-Istence: I agree that as the size of the navigation objects grow, it may become become rather unwieldly; though I'll have to dig deeper in the code to complete my understanding as to the size tipping point.
I'm working on a Symfony project (my first) where I have to retrieve, from my Widget class, a set of widgets that belong to a Page. Before returning the results, though, I need to verify--against an external service--that the user is authorized to view each widget. If not, of course, I need to remove the widget from the result set.
Using CakePHP or Rails, I'd use callbacks, but I haven't found anything similar for Symfony. I see events, but those seem more relevant to controllers/actions if I'm reading things correctly (which is always up for discussion). My fallback solution is to override the various retrieval methods in the WidgetPeer class, divert them through a custom method that does the authorization and modifies the result set appropriately. That feels like massive overkill, though, since I'd have to override every selection method to ensure that authorization was done without future developers having to think about it.
It looks like behaviors could be useful for this (especially since it's conceivable that I might need to authorize other class instances in the future), but I can't find any decent documentation on them to make a qualified evaluation.
Am I missing something? It seems like there must be a better way, but I haven't found it.
First of all, I think behavior-based approach is wrong, since it increases model layer coupling level.
There's sfEventDispatcher::filter() method which allows you to, respectively, filter parameters passed to it.
So, draft code will look like:
<somewhere>/actions.class.php
public function executeBlabla(sfWebRequest $request)
{
//...skip...
$widgets = WidgetPeer::getWidgetsYouNeedTo();
$widgets = $this->getEventDispatcher()->filter(new sfEvent($this, 'widgets.filter'), $widgets));
//...skip...
}
apps/<appname>/config/<appname>Configuration.class.php
//...skip...
public function configure()
{
$this->registerHandlers();
}
public function registerHandlers()
{
$this->getEventDispatcher()->connect('widgets.filter', array('WidgetFilter', 'filter'));
}
//...skip
lib/utility/WidgetFilter.class.php
class WidgetFilter
{
public static function filter(sfEvent $evt, $value)
{
$result = array();
foreach ($value as $v)
{
if (!Something::isWrong($v))
{
$result[] = $v;
}
}
}
}
Hope you got an idea.
Here's some documentation on Symfony 1.2 Propel behaviors: http://www.symfony-project.org/cookbook/1_2/en/behaviors.
Why not just have a 'getAllowedWidgets' method on your Page object that does the checks you're looking for? Something like:
public function getAllowedWidgets($criteria = null, PropelPDO $con = null) {
$widgets = $this->getWidgets($criteria, $con);
$allowed = array();
// get access rights from remote service
foreach($widgets as $widget) {
// widget allowed?
$allowed[] = $widget;
}
return $allowed;
}
However, if you always want this check to be performed when selecting a Page's Widgets then Propel's behaviours are your best bet.
Although, at least in theory, I still think that a behavior is the right approach, I can't find a sufficient level of documentation about their implementation in Symfony 1.4.x to give me a warm and fuzzy that it can be accomplished without a lot of heartburn, if at all. Even looking at Propel's own documentation for behaviors, I see no pre- or post-retrieval hook on which to trigger the action I need to take.
As a result, I took my fallback path. After some source code sifting, though, I realized that it wasn't quite as laborious as I'd first thought. Every retrieval method goes through the BasePeer model's doSelect() method, so I just overrode that one in the customizable Peer model:
static public function doSelect( Criteria $criteria, PropelPDO $con = null ) {
$all_widgets = parent::doSelect( $criteria, $con );
$widgets = array();
foreach ( $widgets as $i => $widget ) {
#if( authorized ) {
# array_push( $widgets, $widget );
#}
}
return $widgets;
}
I haven't wired up the service call for authorization yet, but this appears to work as expected for modifying result sets. When and if I have to provide authorization for additional model instances, I'll have to revisit behaviors to remain DRY, but it looks like this will suffice nicely until then.