Zend_db caching - zend-framework

Is there a way to caching resultsets in Zend_db? For example, I want to run a select query using Zend_db and want this query to be cached to be able to run it faster later.

My advice is that create a initialization method in Bootstrap.php with prefix "_init". for exaple :
/**
*
* #return Zend_Cache_Manager
*/
public function _initCache()
{
$cacheManager = new Zend_Cache_Manager();
$frontendOptions = array(
'lifetime' => 7200, // cache lifetime of 2 hours
'automatic_serialization' => true
);
$backendOptions = array(
'cache_dir' => APPLICATION_PATH . '/cache/zend_cache'
);
$coreCache = Zend_Cache::factory(
'Core',
'File',
$frontendOptions,
$backendOptions
);
$cacheManager->setCache('coreCache', $coreCache);
$pageCache = Zend_Cache::factory(
'Page',
'File',
$frontendOptions,
$backendOptions
);
$cacheManager->setCache('pageCache', $pageCache);
Zend_Registry::set('cacheMan', $cacheManager);
return $cacheManager;
}
By this way, you have created and injected your cache manager with the caches which you need in your app.
Now you can use this cache object where you want to use.
For instance, in your controller or where else :
/**
*
* #return boolean |SimplePie
*/
public function getDayPosts()
{
$cacheManager = Zend_Registry::get('cacheMan');
$cache = $cacheManager->getCache('coreCache');
$cacheID = 'getDayPosts';
if (false === ($blog = $cache->load($cacheID))) {
$blog = Blog::find(array('order' => 'rand()', 'limit' => 1));
$cache->save($blog, $cacheID);
}
// do what you want to do with the daya you fetched.
}

You can use Zend_Cache when you want to save result-sets.
Zend_Db doesn't do any result-set caching itself. It's left for you to do it an application-specific way, because the framework has no way of knowing which result-sets need to be cached for performance reasons, versus those that can't be cached because you need them to be absolutely current. Those are criteria only you as the application developer know.
Just googling for "zend_db cache results" the first match is this blog showing how to use a Zend_Cache object to save a db query result: Zend Framework:: Caching the database query results

Related

TYPO3/Extbase How to create unique slug within create action?

I have slug field in my TCA and in general it works, when adding via Backend > List module, even if I won't input any value the unique eval ensures that slug will be unique, so when I'll create many rows with the same name Foo TYPO3 backend will enshure that it will resolve to unique slugs like foo, foo-1, foo-2, etc. Kudos!:
'slug' => [
'exclude' => true,
'label' => 'Slug',
'displayCond' => 'VERSION:IS:false',
'config' => [
'type' => 'slug',
'generatorOptions' => [
'fields' => ['name'],
'fieldSeparator' => '/',
'replacements' => [
'/' => '',
],
],
'fallbackCharacter' => '-',
'eval' => 'unique',
'default' => '',
'appearance' => [
'prefix' => \BIESIOR\Garage\UserFunctions\SlugPrefix::class . '->getPrefix'
],
],
],
However when creating a new object from my form within new/create actions (typical Extbase CRUD from extension_builder as you can see) like:
public function createAction(Car $newCar)
{
$this->addFlashMessage(
'The object was created. Please be aware that this action is publicly accessible unless you implement an access check. See https://docs.typo3.org/typo3cms/extensions/extension_builder/User/Index.html',
'',
\TYPO3\CMS\Core\Messaging\AbstractMessage::WARNING);
$this->carRepository->add($newCar);
$this->redirect('list');
}
of course slug is note set.
My first idea is to duplicate the logic of TCA type='slug' and just add this functionality with some own JS, AJAX and PHP, however that sounds as overload and time consumption. Especially that I don't want the user to care about slug part at all. Is there any simple API for lookup for a unique slug of the given table that can be used in custom action instead?
Note this question is not about how to handle it with JS, that's just concept. I would like to skip this part for FE user at all, he doesn't need to know what the slug is. Just during creating a new object, I want to get unique value like foo-123 instead.
In addition to Jonas Eberles answer here's another example which also respects the eval configuration of the slug field (can be uniqueInSite, uniqueInPid or simply unique).
use TYPO3\CMS\Core\DataHandling\Model\RecordStateFactory;
use TYPO3\CMS\Core\DataHandling\SlugHelper;
use TYPO3\CMS\Core\Utility\GeneralUtility;
...
public function createAction(Car $newCar)
{
$this->carRepository->add($newCar);
GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class)->persistAll();
$record = $this->carRepository->findByUidAssoc($newCar->getUid())[0];
$tableName = 'tx_garage_domain_model_car';
$slugFieldName = 'slug';
// Get field configuration
$fieldConfig = $GLOBALS['TCA'][$tableName]['columns'][$slugFieldName]['config'];
$evalInfo = GeneralUtility::trimExplode(',', $fieldConfig['eval'], true);
// Initialize Slug helper
/** #var SlugHelper $slugHelper */
$slugHelper = GeneralUtility::makeInstance(
SlugHelper::class,
$tableName,
$slugFieldName,
$fieldConfig
);
// Generate slug
$slug = $slugHelper->generate($record, $record['pid']);
$state = RecordStateFactory::forName($tableName)
->fromArray($record, $record['pid'], $record['uid']);
// Build slug depending on eval configuration
if (in_array('uniqueInSite', $evalInfo)) {
$slug = $slugHelper->buildSlugForUniqueInSite($slug, $state);
} else if (in_array('uniqueInPid', $evalInfo)) {
$slug = $slugHelper->buildSlugForUniqueInPid($slug, $state);
} else if (in_array('unique', $evalInfo)) {
$slug = $slugHelper->buildSlugForUniqueInTable($slug, $state);
}
$newCar->setSlug($slug);
$this->carRepository->update($newCar);
}
with custom finder in the repository to fetch assoc array instead of the mapped object for $racord argument
public function findByUidAssoc($uid)
{
$query = $this->createQuery();
$query->matching(
$query->equals('uid', $uid)
);
return $query->execute(true)[0];
}
Note that the record needs to be persisted before executing above code.
References:
SlugHelper::generate
SlugHelper::buildSlugForUniqueInSite
SlugHelper::buildSlugForUniqueInPid
SlugHelper::buildSlugForUniqueInTable
According to answers from Elias and Jonas, I created a class which simplifies things especially when you have more models to handle
typo3conf/ext/sitepackage/Classes/Utility/SlugUtility.php
<?php
namespace VENDOR\Sitepackage\Utility; // <- to be replaced with your namespace
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\DataHandling\Model\RecordStateFactory;
use TYPO3\CMS\Core\DataHandling\SlugHelper;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/***
*
* This file is part of the "Sitepackage" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* (c) 2020 Marcus Biesioroff <biesior#gmail.com>
* Concept by: Elias Häußler
* Jonas Eberle
*
***/
class SlugUtility
{
/**
* #param int $uid UID of record saved in DB
* #param string $tableName Name of the table to lookup for uniques
* #param string $slugFieldName Name of the slug field
*
* #return string Resolved unique slug
* #throws \TYPO3\CMS\Core\Exception\SiteNotFoundException
*/
public static function generateUniqueSlug(int $uid, string $tableName, string $slugFieldName): string
{
/** #var Connection $connection */
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName);
$queryBuilder = $connection->createQueryBuilder();
$record = $queryBuilder
->select('*')
->from($tableName)
->where('uid=:uid')
->setParameter(':uid', $uid)
->execute()
->fetch();
if (!$record) return false;
// Get field configuration
$fieldConfig = $GLOBALS['TCA'][$tableName]['columns'][$slugFieldName]['config'];
$evalInfo = GeneralUtility::trimExplode(',', $fieldConfig['eval'], true);
// Initialize Slug helper
/** #var SlugHelper $slugHelper */
$slugHelper = GeneralUtility::makeInstance(
SlugHelper::class,
$tableName,
$slugFieldName,
$fieldConfig
);
// Generate slug
$slug = $slugHelper->generate($record, $record['pid']);
$state = RecordStateFactory::forName($tableName)
->fromArray($record, $record['pid'], $record['uid']);
// Build slug depending on eval configuration
if (in_array('uniqueInSite', $evalInfo)) {
$slug = $slugHelper->buildSlugForUniqueInSite($slug, $state);
} else if (in_array('uniqueInPid', $evalInfo)) {
$slug = $slugHelper->buildSlugForUniqueInPid($slug, $state);
} else if (in_array('unique', $evalInfo)) {
$slug = $slugHelper->buildSlugForUniqueInTable($slug, $state);
}
return $slug;
}
}
Usage in any place, like controller. Scheduler task, repository, etc. Keep in mind that record should be saved before (it may be created by Extbase, or just with plain SQL), just need to have created uid and be valid TYPO3 record.
use VENDOR\Sitepackage\Utility\SlugUtility;
use \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
...
$pageSlug = SlugUtility::generateUniqueSlug(
5, // int $uid UID of record saved in DB
'pages', // string $tableName Name of the table to lookup for uniques
'slug' // string $slugFieldName Name of the slug field
)
// or
$uniqueSlug = SlugUtility::generateUniqueSlug(
123,
'tx_garage_domain_model_car',
'slug'
);
// or according to the original question,
// if you created new model object with Extbase,
// persist it, create unique slug with SlugUtility
// set the slug property to the created model object and finally update
public function createAction(Car $newCar)
{
$this->carRepository->add($newCar);
GeneralUtility::makeInstance(PersistenceManager::class)->persistAll();
$uniqueSlug = SlugUtility::generateUniqueSlug(
$newCar->getUid(),
'tx_garage_domain_model_car',
'slug'
);
if($uniqueSlug) {
$newCar->setSlug($uniqueSlug);
$this->carRepository->update($newCar);
}
$this->redirect('list');
}
// no need for second call to persistAll()
// as Extbase will call it at action's finalizing.
// etc.
You can use the SlugHelper directly. The API was obviously not made very fluent for that use case but it works...
$this->carRepository->add($newCar);
// probably you need to persist first - I am not sure if this is really necessary
$this->objectManager()->get(
\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class
)->persistAll();
$table = 'tx_garage_domain_model_car';
$field = 'slug';
// a stripped down record with just the necessary fields is enough
$record = ['name' => $newCar->getName()];
$pid = $this->settings->persistence->...
$slugHelper = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
\TYPO3\CMS\Core\DataHandling\SlugHelper::class,
$table,
$field,
$GLOBALS['TCA'][$table]['columns'][$field]['config']
);
$newCar->slug = $slugHelper->generate($record, $pid);

Security Group subpanel doesn't exist for quotes, contrats, invoices, and events modules

I am using suiteCRM 7.7.4 (Sugar Version 6.5.24) and I need to use Security group subpanel in quotes, contracts, invoices and events modules, but for some reasons I can't find it ! I did some researches and I found that this subpanel doesn't appear by default for custom modules.. some developers recommand to do not use the studio to build this kind of relationship, because simply it will not work ! for paid version of sugarCRM they say that there was a tool called "hookup tool" that creates the relationship for you... but As I am using a free version I can't use it !
Do you have any idea ?
Thank you very much !
I finaly find a solution :
Adding this few lines to "modules/AOS_Contracts/metadata/subpaneldefs.php" :
'securitygroups' => array(
'top_buttons' => array(array('widget_class' => 'SubPanelTopSelectButton', 'popup_module' => 'SecurityGroups', 'mode' => 'MultiSelect'),),
'order' => 900,
'sort_by' => 'name',
'sort_order' => 'asc',
'module' => 'SecurityGroups',
'refresh_page' => 1,
'subpanel_name' => 'default',
'get_subpanel_data' => 'SecurityGroups',
'add_subpanel_data' => 'securitygroup_id',
'title_key' => 'LBL_SECURITYGROUPS_SUBPANEL_TITLE',
),
QRR
Verifying permissions.
follow this following steps:
1. Go to Admin
2. Go to studio
3. Select your module where you want subpanel like "invoices"
4. Go to relationship
5. Add 1 to many relationship with Security group module.
6. Now repair rebuild you will find the subapnel in invoice module.
When you create 1 to many relationship with any module it will create the subpanel.
IF its not working then go for custom subpanel.
Refer this linnk I put code from same link it worked for me
This tutorial should hopefully help you to create a new subpanel under the Contacts module in Sugar using a custom link class and driven by SugarCRM 7's new SugarQuery API.
Create a new link class
This should go into custom/modules/<YourModule>/YourNewLink.php and this class will act as the custom functionality that will build your link between the two records.
<?php
/**
* Custom filtered link
*/
class YourNewLink extends Link2
{
/**
* DB
*
* #var DBManager
*/
protected $db;
public function __construct($linkName, $bean, $linkDef = false)
{
$this->focus = $bean;
$this->name = $linkName;
$this->db = DBManagerFactory::getInstance();
if (empty($linkDef)) {
$this->def = $bean->field_defs[$linkName];
} else {
$this->def = $linkDef;
}
}
/**
* Returns false if no relationship was found for this link
*
* #return bool
*/
public function loadedSuccesfully()
{
// this link always loads successfully
return true;
}
/**
* #see Link2::getRelatedModuleName()
*/
public function getRelatedModuleName()
{
return '<Your_Module>';
}
/**
*
* #see Link2::buildJoinSugarQuery()
*/
public function buildJoinSugarQuery($sugar_query, $options = array())
{
$joinParams = array('joinType' => isset($options['joinType']) ? $options['joinType'] : 'INNER');
$jta = 'active_other_invites';
if (!empty($options['joinTableAlias'])) {
$jta = $joinParams['alias'] = $options['joinTableAlias'];
}
$sugar_query->joinRaw($this->getCustomJoin($options), $joinParams);
return $sugar_query->join[$jta];
}
/**
* Builds main join subpanel
* #param string $params
* #return string JOIN clause
*/
protected function getCustomJoin($params = array())
{
$bean_id = $this->db->quoted($this->focus->id);
$sql = " INNER JOIN(";
$sql .= "SELECT id FROM accounts WHERE id={$bean_id}"; // This is essentially a select statement that will return a set of ids that you can match with the existing sugar_query
$sql .= ") accounts_result ON accounts_result.id = sugar_query_table.id";
return $sql;
}
}
The argument $sugar_query is a new SugarQuery object, the details of which are documented here. What you essentially need to do is extend this query with whatever join/filters you wish to add. This is done in the inner join I've specified.
Note: The inner join can get really complicated, so if you want a real working example, checkout modules/Emails/ArchivedEmailsLink.php and how the core sugar team use this. I can confirm however that this does work with custom joins.
Here is the getEmailsJoin to help you understand what you can actually produce via this custom join.
/**
* Builds main join for archived emails
* #param string $params
* #return string JOIN clause
*/
protected function getEmailsJoin($params = array())
{
$bean_id = $this->db->quoted($this->focus->id);
if (!empty($params['join_table_alias'])) {
$table_name = $params['join_table_alias'];
} else {
$table_name = 'emails';
}
return "INNER JOIN (\n".
// directly assigned emails
"select eb.email_id, 'direct' source FROM emails_beans eb where eb.bean_module = '{$this->focus->module_dir}'
AND eb.bean_id = $bean_id AND eb.deleted=0\n" .
" UNION ".
// Related by directly by email
"select DISTINCT eear.email_id, 'relate' source from emails_email_addr_rel eear INNER JOIN email_addr_bean_rel eabr
ON eabr.bean_id = $bean_id AND eabr.bean_module = '{$this->focus->module_dir}' AND
eabr.email_address_id = eear.email_address_id and eabr.deleted=0 where eear.deleted=0\n" .
") email_ids ON $table_name.id=email_ids.email_id ";
}
Add a new vardef entry for the link field.
For this example, I'm going to create the custom link on the contacts module. So this code goes in custom/Extension/modules/Contacts/Ext/Vardefs/your_field_name.php
<?php
$dictionary["Contact"]["fields"]["your_field_name"] = array(
'name' => 'active_other_invites',
'type' => 'link',
'link_file' => 'custom/modules/<YourModule>/YourNewLink.php',
'link_class' => 'YourNewLink',
'source' => 'non-db',
'vname' => 'LBL_NEW_LINK',
'module' => '<YourModule>',
'link_type' => 'many',
'relationship' => '',
);
Add the new link as a subpanel
This goes under custom/Extension/modules/Contacts/Ext/clients/base/layouts/subpanels/your_subpanel_name.php
<?php
$viewdefs['Contacts']['base']['layout']['subpanels']['components'][] = array (
'layout' => 'subpanel',
'label' => 'LBL_NEW_LINK',
'context' =>
array (
'link' => 'your_field_name',
),
);
Add the label
Under
custom/Extension/modules/Contacts/Ext/Language/en_us.new_link.php
<?php
$mod_strings['LBL_ACTIVE_OTHER_INVITES'] = 'Your New Link';
Quick Repair and Rebuild
That should hopefully get you started. Keep an eye on the sugarlogs while you're debugging your queries. I also found using xdebug and SugarQueries compileSql function invaluable in figuring out what I needed to do to get a working INNER JOIN statement.
I've found this to be a surprisingly powerful solution, it means that if you need to show information related to a module that might be a few joins away, this allows you to create the links manually without having to create pointless related fields in-between the two.

Fuel PHP - to_array() method and multiple belongs_to relationships and eager loading

I am attempting to migrate some legacy data models/schemas to a fuel API, and have run into an odd issue with the to_array() method on a model that has two $_belongs_to properties.
When I load the model without the to_array() method, I properly receive both related items with eager loading, but as soon as I pass them through this function to convert the data to make it digestable by the new API, it will strip out the second $_belongs_to property. If I re-order the props in the $belongs_to array, it will show whichever item is first in the array.
My question is, how can I convert this data to an array without losing the second relationship?
Here are some cleaned up examples for ease of reference:
Transaction Model:
protected static $_belongs_to = array(
'benefactor' => array(
'key_from' => 'from_user_id',
'model_to' => 'Model\\Legacy\\User',
'key_to' => 'id',
),
'user' => array(
'key_from' => 'user_id',
'model_to' => 'Model\\Legacy\\User',
'key_to' => 'id',
),
);
Transaction Controller:
$result = array();
$id = $this->param('id');
if (!empty($id)) {
$transaction = Transaction::find($id, array('related' => array('user', 'benefactor',)));
if (!empty($transaction)) {
// Works -- both benefactor and user are returned
$result['transaction_works'] = $transaction;
// Does not work -- only the benefactor is returned
$result['transaction_doesnt_work'] = $transaction->to_array();
}
}
return $this->response($result);
For any googlers looking for help on this issue, I was seemingly able to return all relationships by simply executing the to_array() method before setting the return/results variable:
$result = array();
$id = $this->param('id');
if (!empty($id)) {
$transaction = Transaction::find($id, array('related' => array('user', 'benefactor',)));
if (!empty($transaction)) {
$transaction->to_array();
$result['transaction_works'] = $transaction;
}
}
return $this->response($result);
Good luck!

Zend Framework Router Hostname and Multi-Language support

Last two days I was fighting with Zend Framework Router and still didn't find solution.
Existing project has 2 different modules which work with the same domain e.g. www.domain1.com:
default - Contains public information, can be accessed via www.domain1.com
admin - Administrator interface, can be accessed via www.domain1.com/admin
Project is multi-langual and to keep language code it is transmitted as first parameter of every URL, e.g. www.domain1.com/en/ www.domain1.com/en/admin
Part of code which takes care is next plugin:
class Foo_Plugin_Language extends Zend_Controller_Plugin_Abstract
{
const LANGUAGE_KEY = 'lang';
public function routeStartup(Zend_Controller_Request_Abstract $request)
{
$languagesRegexp = implode('|', array_map('preg_quote', $this->_bootstrap->getLanguages()));
$routeLang = new Zend_Controller_Router_Route(
':' . self::LANGUAGE_KEY,
array(self::LANGUAGE_KEY => $this->_bootstrap->getLanguage()->toString()),
array(self::LANGUAGE_KEY => $languagesRegexp)
);
$router = $this->getFrontController()->getRouter();
$router->addDefaultRoutes();
$chainSeparator = '/';
foreach ($router->getRoutes() as $name => $route) {
$chain = new Zend_Controller_Router_Route_Chain();
$chain
->chain($routeLang, $chainSeparator)
->chain($route, $chainSeparator);
$new_name = $this->_formatLanguageRoute($name);
$router->addRoute($new_name, $chain);
}
protected function _formatLanguageRoute($name)
{
$suffix = '_' . self::LANGUAGE_KEY;
if (substr($name, -strlen($suffix)) == $suffix) return $name;
return $name . '_' . self::LANGUAGE_KEY;
}
public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
$lang = $request->getParam(self::LANGUAGE_KEY, null);
$this->_bootstrap->setLanguage($lang);
$actual_lang = $this->_bootstrap->getLanguage()->toString();
$router = $this->getFrontController()->getRouter();
$router->setGlobalParam(self::LANGUAGE_KEY, $lang);
// Do not redirect image resize requests OR get js, css files
if (preg_match('/.*\.(jpg|jpeg|gif|png|bmp|js|css)$/i', $request->getPathInfo())) {
return true;
}
// redirect to appropriate language
if ($lang != $actual_lang) {
$redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
$params = array(self::LANGUAGE_KEY => $actual_lang);
$route = $this->_formatLanguageRoute($router->getCurrentRouteName());
return $redirector->gotoRouteAndExit($params, $route, false);
}
}
}
One of the first question is what do you think about such way to provide multi-lang support. What I've noticed is that all this chains dramatically decrease operation speed of the server, response time from the server is about 4 seconds...
Main question is: Currently I have to implement such feature: I have domain www.domain2.com that should work just with single module e.g. "foobar"... and it should be available via second url... or, of course, it should work like www.domain1.com/en/foobar by default...
To provide this functionality in Bootstrap class I'be implemented such part of code
// Instantiate default module route
$routeDefault = new Zend_Controller_Router_Route_Module(
array(),
$front->getDispatcher(),
$front->getRequest()
);
$foobarHostname = new Zend_Controller_Router_Route_Hostname(
'www.domain2.com',
array(
'module' => 'foobar'
)
);
$router->addRoute("foobarHostname", $foobarHostname->chain($routeDefault));
And that is not working and as I've found routeDefault always rewrite found correct model name "foobar" with value "default"
Then I've implemented default router like this:
new Zend_Controller_Router_Route(
':controller/:action/*',
array(
'controller' => 'index',
'action' => 'index'
);
);
But that still didn't work, and started work without language only when I comment "routeStartup" method in Foo_Plugin_Language BUT I need language support, I've played a lot with all possible combinations of code and in the end made this to provide language support by default:
class Project_Controller_Router_Route extends Zend_Controller_Router_Route_Module
{
/**
* #param string $path
* #param bool $partial
* #return array
*/
public function match($path, $partial = false)
{
$result = array();
$languageRegexp = '%^\/([a-z]{2})(/.*)%i';
if (preg_match($languageRegexp, $path, $matches)) {
$result['lang'] = $matches[1];
$path = $matches[2];
}
$parentMatch = parent::match($path);
if (is_array($parentMatch)) {
$result = array_merge($result, $parentMatch);
}
return $result;
}
}
So language parameter was carefully extracted from path and regular processed left part as usual...
But when I did next code I was not able to get access to the foobar module via www.domain2.com url, because of module name in request was always "default"
$front = Zend_Controller_Front::getInstance();
/** #var Zend_Controller_Router_Rewrite $router */
$router = $front->getRouter();
$dispatcher = $front->getDispatcher();
$request = $front->getRequest();
$routerDefault = new Project_Controller_Router_Route(array(), $dispatcher, $request);
$router->removeDefaultRoutes();
$router->addRoute('default', $routerDefault);
$foobarHostname = new Zend_Controller_Router_Route_Hostname(
'www.domain2.com',
array(
'module' => 'foobar'
)
);
$router->addRoute("foobar", $foobarHostname->chain($routerDefault));
Instead of summary:
Problem is that I should implement feature that will provide access for the secondary domain to the specific module of ZendFramework, and I should save multi-language support. And I cannot find a way, how to manage all of this...
Secondary question is about performance of chain router, it makes site work very-very slow...
The way I have solved problem with multilanguage page is in this thread:
Working with multilanguage routers in Zend (last post).
Ofcourse my sollution need some caching to do, but I think it will solve your problem.
Cheers.

unserialize cache zend doctrine2

Hi can you help me please! I want to use zend cache file since im in shared hosting.
So the problem is i cant retrieve cached doctrine file result. data has serialized coz it 's object. $cache->load($id) return as array i can't unserialize back.
Please i will glad if you could help me or suggest something. Here my code.
$request = $this->getRequest();
$slug = $request->getParam('slug');
$front = Zend_Controller_Front::getInstance();
$bootstrap= $front->getParam('bootstrap');
$cache = $bootstrap->getResource('cachemanager')->getCache('content');
$cacheId = md5('news_' . $slug);
if ( !($news = **unserialize($cache->load($cacheId))**) ) {
$news = $this->_em->getRepository('Custom\Entity\News')
->findOneBySlug($slug);
$cache->save($news, $cacheId);
var_dump('if u see me that mean not from cached');
}
$page = new Zend_Navigation_Page_Mvc(array(
'label' => $news[0]->getTitle(),
'route' => 'news-view',
'module' => 'news',
'controller' => 'index',
'action' => 'view',
'params' => array(
'slug' => $news[0]->getAlias())
)
);
$page->setActive(false);
$this->_helper->navigation->getContainer()
->findOneBy('uri', '/category/' . $news[0]->getCategory()->getSlug())
->addPage($page);
$this->view->ogpa = new OpenGraphProtocolArticle();
$this->view->news = $news;
$this->view->headTitle($news[0]->getTitle());
Do not save any Doctrine2 models in Zend_Cache see: https://ssmusoke.com/2012/03/25/doctrine2-day-3-proxies-associations-relationships/
This is because in the background Doctrine2 uses proxy classes which cannot be unserialized by the Zend auto loader, which will cause you a great deal of grief ...
If you are to cache anything, cache the output not the models.