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.
Related
I'm writing a very big extbase extension and have to handle some complicated filter functions. It would be a one liner in pure sql, however, I have to use Extbase ORM for various reasons.
This is about performances of theater plays. Every play has multiple performances, the play is the aggregate root.
I have to find performances within a given time range and that's a no-brainer. However, the category is a child of the parent (play). I do not save a reference of the play in the performance, but in the play (that's because the TCA has to be configured as select-type).
I have to forward the result to the paginator widget, which requires an instance of the QueryResultInterface. When I use the plays as aggregate root and extract the performances from there, it returns ObjectStorage and breaks the widget.
Is it somehow possible to restrict the output if a child of a parent category matches or am I assume right: there's no reasonable chance?
public function findByTimeRangeInBeginTimeAndAttendCategoryAndStartOfBooking(Datetime $startRange, Datetime $endRange, $category = 'undefined') {
$query = $this->createQuery();
$constraints = array();
$constraints[] = $query->greaterThanOrEqual('timebegin', $startRange->getTimestamp());
$constraints[] = $query->lessThanOrEqual('timebegin', $endRange->getTimestamp());
if($category !== 'undefined') {
$constraints[] = $query->equals('play.category.name', $category);
}
$query->matching (
$query->logicalAnd($constraints)
);
$query->setOrderings (
Array('timebegin' => Tx_Extbase_Persistence_Query::ORDER_ASCENDING)
);
return $query->execute();
}
I don't think there is much chance without a reference to the play. I would even say your domain model cannot work out this way. Either you have play as aggregate root and then select the plays and go from there to the performances or the performance has a repository too and in that sense becomes an aggregate root itself, but then you would want to have a reference to the play...
I have to use Extbase ORM for various reasons.
What reasons? Are you sure you really have to?
It won't solve your problem, but you should read this:
http://lists.typo3.org/pipermail/typo3-dev/2011-May/043180.html
I really like the idea to bring MVC and ORM to TYPO3 and I am using Extbase myself, but I'd go with a "regular" TYPO3-Extension or another framework if I had to write complex database-driven stuff.
I am currently a beginner in CakePHP, and have played around with CakePHP 1.3, but recently CakePHP 2.0 has been released.
So far I like it but the only thing is being a pain is the fact that it doesn't return Objects, rather it just returns arrays. I mean, it hardly makes sense to have to do $post['Post']['id']. It is (in my opinion) much more practical to just do $post->id.
Now after Google I stumbled upon this link, however, this kept generating errors about indexes not being defined when using the Form class (guessing this is because it was getting the objectified version rather than the array version).
I am following the Blog tutorial (already have followed it under 1.3 but going over it again for 2.0)
So, anyone know how to achieve this without it interfering with the Form class?
Hosh
Little known fact: Cake DOES return them as objects, or well properties of an object, anyway. The arrays are the syntactical sugar:
// In your View:
debug($this->viewVars);
Shwoing $this is a View object and the viewVars property corresponds with the $this->set('key', $variable) or $this->set(compact('data', 'for', 'view')) from the controller action.
The problem with squashing them into $Post->id for the sake of keystrokes is Cake is why. Cake is designed to be a heavy lifter, so its built-in ORM is ridiculously powerful, unavoidable, and intended for addressing infinity rows of infinity associated tables - auto callbacks, automatic data passing, query generation, etc. Base depth of multidimensional arrays depends on your find method, as soon as you're working with more than one $Post with multiple associated models (for example), you've introduced arrays into the mix and there's just no avoiding that.
Different find methods return arrays of different depths. From the default generated controller code, you can see that index uses $this->set('posts', $this->paginate()); - view uses $this->set('post', $this->Post->read(null, $id)); and edit doesn't use $this->set with a Post find at all - it assigns $this->data = $this->Post->read(null, $id);.
FWIW, Set::map probably throws those undefined index errors because (guessing) you happen to be trying to map an edit action, amirite? By default, edit actions only use $this->set to set associated model finds to the View. The result of $this->read is sent to $this->data instead. That's probably why Set::map is failing. Either way, you're still going to end up aiming at $Post[0]->id or $Post->id (depending on what you find method you used), which isn't much of an improvement.
Here's some generic examples of Set::map() property depth for these actions:
// In posts/index.ctp
$Post = Set::map($posts);
debug($Post);
debug($Post[0]->id);
// In posts/edit/1
debug($this-viewVars);
debug($this->data);
// In posts/view/1
debug($this-viewVars);
$Post = Set::map($post);
debug($Post->id);
http://api13.cakephp.org/class/controller#method-Controllerset
http://api13.cakephp.org/class/model#method-Modelread
http://api13.cakephp.org/class/model#method-ModelsaveAll
HTH.
You could create additional object vars. This way you wouldn't interfere with Cake's automagic but could access data using a format like $modelNameObj->id; format.
Firstly, create an AppController.php in /app/Controller if you don't already have one. Then create a beforeRender() function. This will look for data in Cake's standard naming conventions, and from it create additional object vars.
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public function beforeRender() {
parent::beforeRender();
// camelcase plural of current model
$plural = lcfirst(Inflector::pluralize($this->modelClass));
// create a new object
if (!empty($this->viewVars[$plural])) {
$objects = Set::map($this->viewVars[$plural]);
$this->set($plural . 'Obj', $objects);
}
// camelcase singular of current model
$singular = lcfirst(Inflector::singularize($this->modelClass));
// create new object
if (!empty($this->viewVars[$singular])) {
$object = Set::map($this->viewVars[$singular]);
$this->set($singular . 'Obj', $object);
}
}
}
Then in your views you can access the objects like so:
index.ctp
$productsObj;
view.ctp
$productObj->id;
All we're doing is adding 'Obj' to the variable names that Cake would already provide. Some example mappings:
Products -> $productsObj
ProductType -> $productTypesObj
I know this is not perfect but it would essentially achieve what you wanted and would be available across all of your models.
While I like the idea Moz proposes there are a number of existing solutions to this problem.
The quickest one I found is https://github.com/kanshin/CakeEntity - but it looks like you might need to refactor it for 2.x - there might even already be a 2.x branch or fork but I didn't look.
I also ran this question couple of time in my head. Now a few Cake based apps later, I see the benefit to be able to branch and merge (am, in_array etc.) result sets more conveniently with arrays than using objects.
The $Post->id form would be a sweet syntactic sugar, but not a real benefit over arrays.
You could write a function that iterates over your public propertys (see ReflectionClass::getProperties) and save it in an array (and return the array).
If you have access to the class, you can implement the ArrayAccess Interface and easily access your object as an array.
P.S.: Sorry, i've never used CakePHP but i think object-to-array conversion doesn't have to be a framework specific problem
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.
I have recently begun working on a PHP/JS Form Class that will also include a SQL Form builder (eg. building simple forms from sql and auto inserts/updates).
I have tried several classes (zend_form, clonefish, PHP Form Builder Class, phorms etc) but as yet haven't come across a complete solution that is simple, customizable and complete (both server side and client side validation, covers all simple html elements and lots of dhtml elements: sorting, wysiwyg, mutli file upload, date picker, ajax validation etc)
My question is why do some "classes" implement elements via an array and others via proper OO class calls.
eg.
Clonefish (popular commercial php class):
$config = Array(
'username' => Array(
'type' => 'inputText',
'displayname' => 'Username',
validation => Array(
Array(
'type' => 'string',
'minimum' => 5,
'maximum' => 15,
),
),
));
$clonefish = new clonefish( 'loginform', 'test.php', 'POST' );
$clonefish->addElements( $config, $_POST );
Then others eg. Zend_Form
$form = new Zend_Form;
$username = new Zend_Form_Element_Text('username');
$username->addValidator(new Zend_Validate_Alnum());
$form->addElement($username);
I realise Zend_Form can pass elements in via an array similar to clonefish but why do this?
Is there any benefit? It seems to make things more complicated especially when using a proper IDE like Komodo.
Any thoughts would be appreciated as I dont want to get too far down the track and realize there was great benefit in using arrays to add elements (although this wouldn't be much of a task to add on).
Cheers
My question is why do some "classes" implement elements via an array and others via proper OO class calls.
For convenience. It's less verbose and it feels less like coding and more like configuration and you need less intimate knowledge of the API.
Btw, the reason you have not yet come across a complete solution that is simple, customizable and complete is because it is not simple. Forms, their validation and rendering is complex, especially if you want to have it customizable for any purpose. ZF's form components are a good example of how to properly decouple and separate all concerns to get the ultimate extensible form builder (including client side code through Zend_Dojo or ZendX_Jquery). But they are also a great example of the complexity required for this. Even with the convenient array configuration, it is damn difficult to make them bend to your will, especially if you need to depart from the default configuration and rendering.
Why to use objects? Becouase they are a much more complex types. Consider the following example (I never useed Zend_Form so I don't even know its architecture):
class MySuperAlnumValidator extends Zend_Validate_Alnum {
protected $forbiddenWords = array();
public function addForbiddenWord($word) {
$this->forbiddenWords[] = $word;
}
// Override Zend_Value_Alnum::validate() - I don't know whether such a method even exists
// but you know what's the point
public function validate() {
parent::validate();
if (in_array($this->value, $this->forbiddenWords) {
throw new Exception('Invalid value.');
}
return $this->value;
}
}
// -----------------------
$validator = new MySuperAlnumValidator();
$validator->addForbiddenWord('admin');
$validator->addForbiddenWord('administrator');
$username->addValidator($validator);
This is only a simple example but when you start writing more complex validators/form fields/etc. then objects are, in principle, the only meaningful tool.
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.