I'm wondering if anyone's got any good advice/experience regarding setting dynamic meta titles in Symfony?
Currently, the solution I'm aware of would be to use the following code to set a title individidually in each action:
$this->getResponse()->setTitle('This is a title');
Because I also need translated titles, I could call the i18n helper in the action to get them included in the extracted XLIFFs. No special SEO stuff needed, just a clean title.
However, the above does require that I tweak every single action separately. View.yml is not suitable as I frequently have multiple actions/templates per module.
Is anyone aware of a better approach in Symfony or is this indeed the right/only way to go?
Thank you.
You should use slots.
In your layout <head> tag:
<title><?php echo get_slot('page_title', __('Default page title here')) ?></title>
And in an action template:
<?php slot('page_title', __('Action page title goes here')) ?>
I think writing separate titles in every action is OK. But if you want to add some global prefix you can use something like this in layout:
<title>SITE NAME — <?= $sf_response->getTitle() ?></title>
Also you can probably manipulate a title per module using preExecute() method in actions.
I personally like using the yml files, it separates 'configuration' from code
To deal with dynamic titles I do the follow:
in apps/frontend/config/app.yml
all:
title_separator: ' - '
title_default: 'TITLE'
in apps/frontend/config/view.yml
default:
metas:
title: %APP_TITLE_DEFAULT%
If you need to have data from your actions put into the title, create file lib/myActions.class.php with the following content:
<?php
class myActions extends sfActions
{
protected function setTitle($string)
{
$this->getResponse()->setTitle($string . sfConfig::get('app_title_separator') . sfConfig::get('app_title_default'));
}
}
?>
(note: modify this as you like, e.g. put default title at front)
Then change your action.class.php to extend myActions instead of sfActions
class memberActions extends myActions
and whenever you need to change the title, just call this in your action
$this->setTitle('This is how I roll');
and you will get the following title (if using the same config as I did above):
This is how I roll - TITLE
$i18n = $this->getContext()->getI18N();
$this->getResponse()->setTitle('Your title' . ' | ' . $i18n->__('your module name'));
Related
I'm learning Zend Framework 2 and walked already through the famous Album tutorial. So I've a basic understanding what’s going on but unfortunately I miss a fundamental part about layouts.
I want to build a basic Web application that supposed to have:
A homepage for guests
A complete different hompage for logged in user
Divide the layout in header, content, footer
I've installed the ZF2 Skeleton Application and the Zfcuser module. The site and registration pages show up like they supposed to.
I'll find all layout files in Application/view and the configured path in Application/config/module.config.php
But I have still a couple of questions:
How can I divide the layout into header, content, footer? (Different templates)
How can I create my own layout.phtml? Is it best practice to overwrite the one in Application/view or should I create my own module that overwrites the complete view_manager array?
How can I load a different layout according a user is logged in or not? The zfcUser module provides an helper for that:
if(!$this->zfcUserIdentity()):
But I don't know where to add it.
Thank you so much in advance. And if anyone has a good link to a tutorial - please post it. I really appreciate it.
For dividing layout template into blocks, you can use Partial view helper (zf2 docs), simple example of layout template:
<html>
<body>
<?php
// render template from view/partials/header.phtml
echo $this->partial('partials/header');
?>
<?php echo $this->content; ?>
<?php
// render template from view/partials/footer.phtml with some additional variables from layout scope
echo $this->partial('partials/footer', [
'variable' => 'value',
]);
?>
</body>
</html>
If it should be new different layout template, just create new one in view/layout path of your module and use it like I showed you above (layout controller plugin).
If you want overwrite layout template (or any other template) from 3rd party module, you can do it in ViewManager config (zf2 docs), example with overwriting login template from ZfcUser module:
// module.config.php
'view_manager' => [
'template_map' => [
'zfc-user/user/login' => '/path/to/custom/login-template',
],
],
There is Layout controller plugin (zf2 docs), which allows you changing the layout per controller action. For example, in your home page controller action, you can set different layout for for logged users, like this:
if($this->zfcUserIdentity()) {
$this->layout('layout/members');
}
Definitely take a look at this (incomplete) ZF2 Quick Start rewrite with some best practices.
Using Yii 1.1.12. I have a CListView with ajax turned off:
<?php $this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_view',
'ajaxUpdate' => false,
)); ?>
The url for the link to the second page looks like:
http://www.example.com/products?Product_page=2
I want the url to look like:
http://www.example.com/products?page=2
How can I achieve this?
(A bonus would be to also describe how to get a url like http://www.example.com/products/page/2)
Update
As pointed out by elbek, I see the CListView has a pager property of type CLinkPager. This in turn has a pages property of type CPagination which has a property pageVar which is described as:
name of the GET variable storing the current page index. Defaults to
'page'.
Sounds like it could be what I'm looking for, not sure how to modify this from the CListView though.
Additionally to change the pageVar you have to modify the $dataProvider. You can do this when you are defining it in the controller action:
$dataProvider=new CActiveDataProvider('Products',array(
'pagination'=>array(
'pageVar'=>'page'
)
));
Alternatively you can of course modify the $dataProvider in the view itself before using it in the CListView : (not recommended-owing to separation of concerns)
$dataProvider->pagination=array('pageVar'=>'page');
$this->widget('zii.widgets.CListView', array(/*rest of your code*/));
But with this change, to make your url look like http://www.example.com/products/page/2 you'll need to slightly change the rule in urlManager from Suvera's answer:
'products/page/<page:\d+>'=>'products/index',
Note: If you don't need that type of url, you don't need the above rule, just specifying the pageVar does it.
enable the urlManager component on your config. And add the following rule at the top.
'urlManager'=>array(
......
'rules'=>array(
'products/page/<Product_page:\d+>'=>'products/index', //Add it in top
...........
...........
),
),
the above rule will create a url something like http://www.example.com/products/page/2
The value part in the rule products/index is products controller and index action (this is important, so point it out to your actual route).
you can even create urls whatever way you want.
ex 1:
'products/<Product_page:\d+>'=>'products/index'
will give you http://www.example.com/products/2
ex 2
'TheAvengers/vs/Loki/<Product_page:\d+>'=>'products/index'
will give you http://www.example.com/TheAvengers/vs/Loki/2
I think CListView has pager attribute(from its parent class)
You can try to set some attributes for this. I think it is CLinkPager instance.
If I want to use the g.link tag to link the text "foo" to the list action of the book controller, I can do that with:
<g:link action='list' controller='book'>foo</g:link>
The code above shows the "tag syntax", but how can I do the same thing using the "method call syntax"? I tried the following:
g.link(action: 'list', controller: 'book', {'foo'})
but it doesn't work. The problem is that I can't figure out how to pass a static piece of text for the body parameter. In the example above, I've tried putting the text in a closure, but this didn't work.
If the code is e.g. inside controller, you can use:
link( controller:'book', action:'list' ) { 'foo' }
It is not necessary to use the 'g' namespace, it is automatically injected.
Like this:
g.link ([uri:'/'], {"hello"})
I generated a custom theme by copying
plugins/sfDoctrinePlugin/data/generator/sfDoctrineModule/default"
into
plugins/sfDoctrinePlugin/data/generator/sfDoctrineModule/mytheme1"
I changed the templates to match my needs (no tables, custom errors display and so on).
./symfony doctrine:generate-module --theme=mytheme1 frontend user User
works as I expected however if I add
$this->embedI18n(array('en','fr'));
to the form class the generator renders the I18n embedded form with and .
Where do this come from? How can I customize it? where are the template files for i18n embedded forms located?
Thanks a lot,
Massimo
Firstly it's not the best idea to overload a theme in the plugin itself. Plugin's shouldn't be touched to allow future updates. You can easily overload the theme in your application.
Secondly something seems to be missing in your post: "to the form class the generator renders the I18n embedded form with and ."
Maybe missing translation indicators are shown?
The template is defined in the formatter of the widgetSchema of your i18n form. By default, it is set to sfWidgetFormSchemaFormatterTable which contains the definition of what you are looking for :
class sfWidgetFormSchemaFormatterTable extends sfWidgetFormSchemaFormatter
{
protected
$rowFormat = "<tr>\n <th>%label%</th>\n <td>%error%%field%%help%%hidden_fields%</td>\n</tr>\n",
$errorRowFormat = "<tr><td colspan=\"2\">\n%errors%</td></tr>\n",
$helpFormat = '<br />%help%',
$decoratorFormat = "<table>\n %content%</table>";
}
You can change this by editing your i18n form class initialization. For example, if you want to display the fields in a list :
public function setup()
{
parent::setup();
$formatter = new sfWidgetFormSchemaFormatterList($this->getWidgetSchema());
$this->getWidgetSchema()->addFormFormatter('list', $formatter);
$this->getWidgetSchema()->setFormFormatterName('list');
}
You can also define your own formatter inheriting from sfWidgetFormSchemaFormatter to match your layout preferences.
In our WebForms apps we serve flash via simple anchor tags like so:
See It
Now, I'm wanting to move that A tag into a call to a Controller/Action using an Html.ActionLink like so:
Html.ActionLink("See It", "DeliverFlash", new {fileName="whatever.swf"})
Then in the controller I'm using a FileStreamResult to push it out....
That "works" in that the flash goes out BUT....
1) It only prompts the user to download the swf I'd like it to just show it like the original implementation does.
2) I have not yet figured out how to pass along those extra parameters of class and params.
Can anyone help please?
Make sure that when you create the FileResult, that you don't set the FileDownloadName property or it will add a Content-Disposition header to specify it as an attachment. See the source code at: http://www.codeplex.com/aspnet. To set the extra parameters, you simply need to use a signature that includes the html options.
<%= Html.ActionLink( "See It", "DeliverFlash",
new { fileName = "whatever.swf" },
new {
#class = "something",
#params = "width, height, yadda, bang"
} ) %>
Note the # in front of class and params as they are C# keywords.