I need a method of inserting javascript which is controller/action specific into a layout. That javascript needs to go inside the <head> of the document, far from where normal content is placed. I already have an infrastructure in place which allows use of multiple views per page, and the Zend_Layout I already have takes full advantage of this:
<?php
$script = $this->layout()->script;
if (!is_null($script)) : ?>
<script type="text/javascript"> // <![CDATA[
<?php echo $script; ?>
// ]]>
</script>
<?php endif; ?>
However, I'd like the script output to be automatically selected, just like the normal view is automatically placed into $this->layout()->content of the layout by default. I understand this facility is provided by the ViewRenderer class. Basically, what I'd like to do is check for an instance of /VIEWPATH/scripts/CONTROLLER/ACTION.js.php, and render it as the script named output segment if it exists.
I could relatively simply create a Zend_Controller_Plugin which would automatically do that in post dispatch, but then controllers would have no way of setting values on the script's view. I also would need some way of replicating how the ViewRenderer controller plugin is inflecting the controller and action names.
Ideally I'd just somehow tack this on to the ViewRenderer helper, but it doesn't seem to support that kind of thing.
Am I going about this entirely wrong? Is there some mechanism for embedding page specific Javascript built into the framework? (I can't be the only person with this problem....)
Billy3
Extending my comment
Here is the doc for what are you looking for:
http://framework.zend.com/manual/1.12/en/zend.view.helpers.html#zend.view.helpers.initial.headscript
You can use captureStart() and create your scripts dynamically inside each related view.
With this approach you don't need to create *.js.php files.
I think there is no build in mechanism. Iam using an small controller plugin like this:
class My_Controller_Plugin_JavaScript extends Zend_Controller_Plugin_Abstract
{
/**
* preDispatch
* Check controller name, and include javaScript library
*
* #param Zend_Controller_Request_Abstract $request
* #return void
*/
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$layout = Zend_Layout::getMvcInstance();
$view = $layout->getView();
$controller = $request->getControllerName();
$jsFile = $controller . '-lib.js';
$jsPath = $view->baseUrl() .
'/js/' . $controller .
'/';
$sPath = PUBLIC_PATH . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR;
$sPath .= $controller . DIRECTORY_SEPARATOR . $jsFile;
if (file_exists($sPath)) { // load as last js (offset 100)
$view->headScript()->offsetSetFile(
100,
$jsPath . $jsFile
);
}
}
}
It adds an js file by controller name. Layout iam echoing it
<?= $this->headScript(); ?>
You could extend it to use action to. Iam sure there are better ways, but it works!
Zend Framework had view helpers to add javascript file(first snippet) + text javascript(second snippet)
you could add javascript files
<?php
$this->headScript()->appendFile($this->baseUrl("js/jquery-1.4.2.min"))
->appendFile($this->baseUrl("js/jquery-ui-1.8.2.custom.min"));
$this->headScript()->appendScript("js/dummy.js");
echo $this->headScript();
?>
then in some where else , you could add
<?php $this->headScript()->captureStart() ?>
// start jquery functions
var action = '<?php echo $this->baseUrl ?>';
$('foo_form').action = action;
// end jquery functions
<?php $this->headScript()->captureEnd() ?>
The following assumptions are made:
The script will be appended to the
stack. If you wish for it to replace
the stack or be added to the top, you
will need to pass 'SET' or 'PREPEND',
respectively, as the first argument to
captureStart(). The script MIME type
is assumed to be 'text/javascript'; if
you wish to specify a different type,
you will need to pass it as the second
argument to captureStart(). If you
wish to specify any additional
attributes for the tag, pass
them in an array as the third argument
to captureStart().to captureStart().
source : http://framework.zend.com/manual/en/zend.view.helpers.html
Related
I'm trying to figure out how to use one of my view elements inside of a controller...
I know, I know: "Don't do that!" (99% of the time this is the correct answer)
But I think I actually have a good reason. The action is handling an AJAX request which returns markup. The returned markup is a list which I display everywhere else using an element. So in an effort to keep my code DRY, I think it's appropriate to do this here.
Is this possible?
Easy:
$view = new View($this, false);
$content = $view->element('my-element', $params);
Also:
DON'T DO THAT ANYMORE!!!
Sometimes, you need to render a CakePhp element from a view and inject its content into the page using AJAX the same time. In this case rendering element as a regular view from controller is better than creating a dedicated view that just contains <?php echo $this->element('some_element') ?>, and may be done this way:
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
I know this is an old question and other people have already given basically the same answer, but I want to point out that this approach (provided by Serge S.) ...
<?php
public function ajax_action() {
// set data used in the element
$this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));
// disable layout template
$this->layout = 'ajax';
// render!
$this->render('/Elements/some_element');
}
...is not a hacky workaround, but is in fact the recommended approach from the CakePHP docs for this common and legitimate use case:
If $view starts with ‘/’, it is assumed to be a view or element file
relative to the /app/View folder. This allows direct rendering of
elements, very useful in AJAX calls.
(Again: Credit to Serge S. for the code above)
$this->view = '/Elements/myelement';
You should use a client-side template. You should never return mark-up from a web service or API, just data. Have your JavaScript take the data, and then format it how you wish.
For example:
function getItems() {
$.get('/some/url', function(response) {
if (response.data.length > 0) {
for (var i = 0; i < response.data.length; i++) {
var item = response.data[i];
$('.results').append('<li>' + item.title + '</li>');
}
}
});
};
This is just an example written off the cuff. Obviously you’ll need to write your own implementation.
The way I did any ajax handling in Cake was to have my own AjaxController. Any interaction of ajax-kind goes there, which in-turn uses their own views (and view partials / elements). That way you can keep your code DRY and isolate and propagate all ajax use-cases there.
Example excerpt:
<?php
class AjaxController extends AppController {
/**
* (non-PHPdoc)
* Everything going to this controller should be accessed by Ajax. End of story.
* #see Controller::beforeFilter()
*/
public function beforeFilter() {
parent::beforeFilter();
$this->autoRender = false;
$this->layout = false;
if (!$this->request->is('ajax')) {
$this->redirect('/');
}
}
public function preview() {
if ($this->request->is('ajax')) {
$this->set('data', $this->data);
$this->render('/Elements/ajaxpreview');
}
}
?>
Here's the source: https://github.com/Sobient/dosspirit/blob/master/app/Controller/AjaxController.php
I'm trying to clear a custom placeholder from a viewscript, let's say I have a controller plugin that creates a sidebar:
$bootstrap = Zend_Controller_Front::getInstance()->getParam('bootstrap');
$view = $bootstrap->getResource('view');
$partial = $view->render('partials/_sidebar.phtml');
$view->placeholder('sidebar')->append($partial);
My partial contains my submenu (rendered through Zend_Navigation view helper).
In order to render that sidebar, I have this in my layout:
<?= $this->placeholder('sidebar'); ?>
But what if in some pages I don't want to display my sidebar (login page for example) ? How can I handle these cases?
I thought I could reset/clear my placeholder using $this->placeholder('sidebar')->exchangeArray(array()); but it seems that I can't access my placeholder from a viewscript:
// in /application/modules/default/views/account/login.phtml
<?php $placeholder = $this->placeholder('sidebar');
Zend_Debug::dump($placeholder); ?>
// output:
object(Zend_View_Helper_Placeholder_Container)#217 (8) {
["_prefix":protected] => string(0) ""
["_postfix":protected] => string(0) ""
["_separator":protected] => string(0) ""
["_indent":protected] => string(0) ""
["_captureLock":protected] => bool(false)
["_captureType":protected] => NULL
["_captureKey":protected] => NULL
["storage":"ArrayObject":private] => array(0) {
}
}
Any idea how to do such a thing?
Thanks.
Edit:
My problem was very simple actually, since my plugin was registered and executed in the postDispatch() method, then my viewscript was executed before the plugin and the layout was executed after the plugin.
From now on, what are my options? I can't really declare my sidebar in the preDispatch method because there won't be any script directory set, and therefore I won't be able to determine which view script to execute at this step.
I could also use an action() helper, what do you think? A question has been already asked about it. I still feel like this is not the proper way to do it, and it sounds overkilling to me.
Also, another idea would be to move my plugin into a the preDispatch() method of my controller, but that would lead me to copy/paste on every controller my sidebar, or create a baseController, but I still don't like this idea, I feel like I'm doing it wrong.
Any idea?
You were pretty close actually. You just needed to add the name of your placeholder, sidebar in this case:
$this->placeholder('sidebar')->exchangeArray(array()); should work.
See http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.placeholder
EDIT:
That's strange that the above did not work for you. I tested this out on my own ZF website and it worked. Possibly different scenario. I don't know.
Here is another alternative, albeit more involved.
//Get the placeholder object directly
$placeholderObj = $this->getHelper('placeholder')
//This may be why you were getting null in your example above. Since your were using the magic method in the View class to call the placeholder view helper method directly.
//For example you could call the placeholder like so:
$placeholderObj->placeholder('sidebar')->set("Some value");
//Now the alternative way to remove the placeholder value is as follows.
$registry = $placeholderObj->getRegistry()
$registry->deleteContainer('sidebar');
EDIT3
It should work as a Zend_Controller_Plugin using preDispatch hook. I think the only you need to make sure of is that you order that plugin after you setting your view script/helper paths and layout paths. This can be done in the same plugin or in another plugin.
Here is a code example based on something I use on my website. The relevant part is just the stuff in preDispatch where I set the view script paths, etc
class RT_Theme extends Zend_Controller_Plugin_Abstract
{
private $_sitename;
private $_assets;
private $_appPath;
private $_appLibrary;
/**
* Constructor
*
* #param string $sitename
* #param SM_Assets $assets
* #param string $appPath
* #param string $appLibrary
*/
public function __construct($sitename, $assets, $appPath, $appLibrary)
{
$this->_sitename = $sitename;
$this->_assets = $assets;
$this->_appPath = $appPath;
$this->_appLibrary = $appLibrary;
}
/**
* Sets up theme
*
* Sets up theme template directory and assets locations (js, css, images)
*
* #param Zend_Controller_Request_Abstract $request
*/
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$module = $request->getModuleName();
$controller = $request->getControllerName();
$action = $request->getActionName();
$layout = Zend_Layout::getMvcInstance();
$view = $layout->getView();
$view->sitename = $this->_sitename;
$view->headTitle()->setSeparator(' - ')->headTitle($view->sitename);
$layout->setLayoutPath($this->_appPath . "/html/$module/layout/");
$view->setScriptPath($this->_appPath . "/html/$module/view/");
$view->addScriptPath($this->_appPath . "/html/$module/view/_partials/");
$view->addScriptPath($this->_appLibrary . '/RT/View/Partials/');
$view->addHelperPath('RT/View/Helper', 'RT_View_Helper');
$view->addHelperPath($this->_appPath . '/html/view/helpers','My_View_Helper');
$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$viewRenderer->setView($view);
//Ignore this line. Not relevant to the question
$this->_assets->loadFor($controller, $action);
//Example: Now I can set the placeholder.
$view->placeholder('header_title')->set($view->sitename);
}
}
NOTE: UPDATED MY QUESTION
I am using zend.I have the file "stylesettings.php" under css folder. Having the following line to convert php file to css.
header("Content-type: text/css");
stylesetting.php is under application/css/stylesettings.php
Now i want to get the color code from my DB in stylesettings.php.Here i can write basic DB connection code to get values from DB. I guess there might be another way to get all DB values by using zend. How can we connect DB like "Zend_Db_Table_Abstract" in stylesettings file ?
Is it possible to use zend component in that file ? Kindly advice on this.
I hope you understand.
You are breaking the basic separated model assumed in a MVC framework. For such color code, if it's only used in one place, I would suggest that you output the color in the "style="color: <color>"" in-line style in order to keep the dynamic part in HTML and your CSS file static.
If you really want to do this, then you should consider output your dynamic stylesheet in a URL path other than css, and use the controller/views etc to generate the stylesheet.
i am using the following way to apply color settings in layout.phtml
Use below code in bootstrap file
<?php
require_once 'plugins/StyleController.php';
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {
protected function _initRouter() {
$front = Zend_Controller_Front::getInstance ();
$front->setControllerDirectory ( dirname ( __FILE__ ) . '/controllers' );
$router = $front->getRouter ();
$front->registerPlugin ( new StyleController ( $router ) );
}
}
Created folder plugins under application and create a new file called stylecontroller.php
<?php
class StyleController extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$layout = Zend_Layout::getMvcInstance();
$view = $layout->getView();
/* code for getting color settings */
$this->settings = new Admin_Model_DbTable_Settings();
$view->colorsettings = $this->settings->getStyleSettings();
//print_obj($view->colorsettings);
}
}
?>
Also to get color code,
<?php
class Admin_Model_DbTable_Settings extends Zend_Db_Table_Abstract
{
public function getStyleSettings()
{
$select = $this->_db->select()
->from('style_settings');
$result = $this->getAdapter()->fetchAll($select);
return $result['0'];
}
}
?>
By using above code i can able to use $this->colorsettings in layout page.
This one fix my dynamic color in layout but not in other template files.
What would be the best way to check for the current URI segment in a CodeIgniter view? What I am trying to do is use the current URI segment [i.e. $this->uri->segment(1)] in order to highlight the current page on the navigation bar.
The best that I have figured out is to do
$data['current_uri'] = $this->uri->segment(1);
$this->load->view('header', $data);
in each of my controllers and then in the header.php file, I check the $current_uri variable to determine which part of the navigation should be highlighted. As you know, this is a very tedious way of doing it, but I'm at a loss of a better way to do this.
It may even be possible to extend the default Controller class to pass the current URI segment, but I'm not sure if this would work, or even how to go about doing it.
I myself use an extra function similar to anchor(). I call it active_anchor(), and it takes all the same parameters plus another (the uri). The function then adds the class 'active' if the uri string passed matches the active_anchor() url paramter.
Then the function returns using the anchor function (all that the function did was determine if the link needed the class 'active' or not.
EDIT:
I just put this code in a file called 'MY_url_helper.php'. That way, when the url helper is loaded (I actually auto load that one since pretty much all of my views use it anyway.) This is just some quick code too, pretty sure it would work. Basically it takes the same arguments as the anchor() function, but also the $key variable. It appends a class of "active" to the anchor tag if the key and url match.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
if ( ! function_exists('active_anchor'))
{
function active_anchor($url = NULL, $title = NULL, $key = NULL, $params = array())
{
if ($url && $key)
{
if($key == $url)
{
if (array_key_exists ('class' , $params))
{
$params['class'] .= ' active';
}
else
{
$params['class'] = 'active';
}
}
}
return anchor($url, $title, $params);
}
}
Simple way to check the uri segment in view,
Add some code if matches found.
<li class="<?php echo (strcmp($this->uri->segment(2),'test')==0)?'active':''; ?>"><li>
<li class="<?php echo (strcmp($this->uri->segment(2),'test1')==0)?'active':''; ?>"><li>
<li class="<?php echo (strcmp($this->uri->segment(2),'test2')==0)?'active':''; ?>"><li>
I also had the same problem when I was building a customer website in Cakephp, passing those strings for every menu item from controller to view, then checking again in view for implementing the highlighting to tedious to say the least.
For some of my projects now, I have been implementing the same by storing the page information for each of the navigation menu pages in database, things like page name, url, title, position in navigation menu etc.
Then at the start of controller, I store all this data in an array say $pageinfo.
I handle the navigation functionality via a single controller that checks the URI segment and loads the content based on that.
The highlighting part is left to an if statement when generating the view, where I compare each page name to the information I dumped in $pageinfo.
Something like this...
foreach ($navi_menu as $links) {
if ( $links['title'] == $pageinfo['title'] ) {
// Highlight here
}
else {
// No highlight
}
}
This way I don't need to pass the string constants (uri segments in your case) to the view. This CMS-kinda-approach allows me to flexible in adding further items to my menu, without adding more code.
I remember getting this from a codeigniter wiki, can't find the link to it right now.
this simple way and running well for me..
<li class="<?=($this->uri->segment(2)==='test')?'current-menu-item':''?>"><?php echo anchor ('home/index','HOME'); ?></li>
<li class="<?=($this->uri->segment(2)==='blog')?'current-menu-item':''?>"><?php echo anchor ('home/blog','BLOG'); ?></li>
<li class="<?=($this->uri->segment(2)==='bla..bla')?'current-menu-item':''?>"><?php echo anchor ('home/blog','bla..bla'); ?></li>
uri_segment(2) that mean function in ur controller.
but have a weakness, i have trouble if i put view in index controller, so im not use function index ( toruble in uri segment, make 2 current page in same time... read this http://ellislab.com/codeigniter/user-guide/libraries/uri.html
I'll probably get flamed for suggesting a client-side approach, but this is something I've used in the past to mark the current page as highlighted:
var path = location.pathname.substring(1);
if ( path )
$('#navigation a[href$="' + path + '"]').parents('li').attr('class', 'current');
In every CodeIgniter project I gather some basic information about the request in MY_Controller.
I extend the core controller and put in some initialization logic that needs to happen on every page. This includes getting the information about the controller and method, which is passed to the view. As a short example:
class MY_Controller extends CI_Controller
{
protected $_response_type = 'html';
protected $_secure;
protected $_dir;
protected $_controller;
protected $_method;
protected $_template;
protected $_view;
protected $_user;
public function __construct()
{
parent::__construct();
// Gather info about the request
$this->_secure = ! empty($_SERVER['HTTPS']);
$this->_dir = $this->router->fetch_directory();
$this->_controller = $this->router->fetch_class();
$this->_method = $this->router->fetch_method();
// set the default template and view
$this->_template = 'default';
$this->_view = $this->_dir . $this->_controller . '/' . $this->_method;
}
/**
* Performs operations right before data is displayed.
*
* #access public
* #return void
*/
public function _pre_display()
{
if ($this->_response_type === 'html') {
$this->load->vars(array(
'user' => $this->_user
));
}
elseif ($this->_response_type === 'json') {
$this->_template = 'json';
$this->_view = NULL;
}
else {
show_error('Invalid response type.');
}
$this->load->vars(array(
'is_secure' => $this->_secure,
'controller' => $this->_controller,
'method' => $this->_method
));
}
}
Now in a view such as the navigation you can use that information like this:
<a href="<?php echo site_url('products') ?>"<?php echo ($controller === 'products') ? ' class="selected"' : ''; ?>>Products</a>
I like it because with routes or rewrites you may access controllers and methods from different URLs. This way you set whether the link is active based on the controller/method that is serving up the content and not based on the URL.
Also, this information can be reused within your controllers or views for any reason and you are not continually asking the router or uri to calculate the information (which not only is inefficient, but is cumbersome to write over and over).
After reading this thread: How to force browser to reload cached CSS/JS files?
I would like to know if there is any built-in function or easy way in Symfony that automatically forces a reload by appending a random querystring or timestamp to the link when it has discovered that javascript / css file has been modified. (Normally, people use the use_javascript function to generate the <script> tag)
There is no built-in mechanism, but a little creativity means you can do this just about anywhere in your code, from view.yml to layout.php to each individual action.
The view.yml method is easy enough:
apps/frontend/config/view.yml:
stylesheets: [main?v=<?php echo time() ?>, reset?v=<?php echo time() ?>, layout?v=<?php echo time() ?>]
Although I think this is a little too active, and I tend to use either the SVN revision or a overall project version number:
stylesheets: [main?v=<?php echo sfConfig('app_project_version') ?>, reset?v=<?php echo sfConfig('app_project_version') ?>, layout?v=<?php echo sfConfig('app_project_version') ?>]
where app_project_version is set in apps/frontend/config/app.yml. Methods for layout.php and actionSuccess.php should be easy enough from here:
<?php use_stylesheet('blah?v='.sfConfig::get('app_project_version')); ?>
instead of setting a version for each stylesheet you include, it is better to have it done automatically for all included stylesheets, no matter if you use view.yml or use_stylesheet() method. You need to implement this helper method and
include the helper in your applications settings.yml, so that it becomes available to alle your actions.
`
function include_versioned_stylesheets()
{
$response = sfContext::getInstance()->getResponse();
sfConfig::set('symfony.asset.stylesheets_included', true);
$html = '';
foreach ($response->getStylesheets() as $file => $options) {
$filepath = sfConfig::get('sf_web_dir') . '/' . stylesheet_path($file);
if(file_exists($filepath)) {
$file .= '?v=' . filectime($filepath);
}
$html .= stylesheet_tag($file, $options);
}
echo $html;
}
`
in your layout.php call this inside your header area. make sure there is no further call to include_stylesheets(), as this is an extended version to it.
same can be done with include_javascripts.