[Part 5] - Customising the view: Twig extensions, The sidebar and Assetic¶
Panoramica ¶
In questo capitolo continuerà la costruzione per il frontend di symblog. Andiamo a modificare la home page per visualizzare le informazioni relative ai commenti per un post sul blog con indirizzamento SEO aggiungendo il titolo del blog per l'URL. Si andrà anche a lavorare sulla barra laterale per aggiungere 2 comuni componenti blog ; La Tag Cloud e i commenti più recenti. Esploreremo i vari ambienti in Symfony2 per imparare a gestire symblog nell'ambiente di produzione. Il motore Twig dei template sarà esteso per fornire un nuovo filtro, e si introduce Assetic per gestire i file del sito di asset. Alla fine di questo capitolo avrete i commenti integrati nella home page, con un tag cloud e la lista decrescente degli ultimi commenti sulla barra laterale Avrete anche fatto lavorare symblog in ambiente di produzione.
La Homepage - Blog e commenti ¶
Finora il sito elenca le ultime entrate del blog, ma non fornisce tutte le informazioni riguardanti i commenti per i blog. Ora che abbiamo costruito l'entità commento andiamo a rivedere la homepage per fornire queste informazioni. Sicome abbiamo istituito il collegamento tra l'entità blog e commento sappiamo che Doctrine 2 sarà in grado di recuperare i commenti per un blog (ricordo che abbiamo aggiunto un membro $ commentsnell'entità l Blog ).Andiamo ad aggiornare il modello View in src / Blogger / BlogBundle / Resources / views / Page / index.html.twig.
{# src/Blogger/BlogBundle/Resources/views/Page/index.html.twig #}
{# .. #}
<footer class="meta">
<p>Comments: {{ blog.comments|length }}</p>
<p>Posted by <span class="highlight">{{ blog.author }}</span> at {{ blog.created|date('h:iA') }}</p>
<p>Tags: <span class="highlight">{{ blog.tags }}</span></p>
</footer>
{# .. #}
Abbiamo usato commenti getter per recuperare i commenti del blog per poi passarli alla collezione attraverso il filtro Twig length Se ora diamo uno sguardo alla homepage con http://symblog.dev/app_dev.php/ si vedrà il numero dei commenti di ogni blog che viene visualizzato.
Come spiegato in precedenza, abbiamo già informato Dcttrine 2 che il membro $ comments dell'entità Blog è mappato all'entità commento. Abbiamo fatto questo nel capitolo precedente con i metadati nell'entità Blog in src / Blogger / BlogBundle / Entità / Blog.php .
// src/Blogger/BlogBundle/Entity/Blog.php
/**
* @ORM\OneToMany(targetEntity="Comment", mappedBy="blog")
*/
protected $comments;
Così sappiamo che Doctrine 2 è a conoscenza del rapporto tra blog e commenti, ma come ha fatto per compilare il membro $ comments con le relative entità Commento. Se vi ricordate il metodo BlogRepository che abbiamo creato (vedi sotto) per ottenere i blogs della homepage senza nessuna selezione per recuperare le entità Commento.
// src/Blogger/BlogBundle/Repository/BlogRepository.php
public function getLatestBlogs($limit = null)
{
$qb = $this->createQueryBuilder('b')
->select('b')
->addOrderBy('b.created', 'DESC');
if (false === is_null($limit))
$qb->setMaxResults($limit);
return $qb->getQuery()
->getResult();
}
Tuttavia, Doctrine 2 utilizza un processo chiamato lazy loading dove le entità Comment vengono recuperati dal database come e quando richiesto, nel nostro caso, quando è stata fatta la chiamata a{{ blog.comments|length }}. Siamo in grado di dimostrare questo processo utilizzando la barra degli strumenti dello sviluppatore. Abbiamo già iniziato ad esplorare le basi della barra degli strumenti di sviluppo è giunta l'ora di introdurre una delle sue caratteristiche più utili, il profiler Doctrine 2 . Il profiler Doctrine 2 può essere accessibile facendo clic sull'ultima icona nella barra degli strumenti di sviluppo. Il numero accanto a questa icona mostra il numero di query eseguite sul database per la richiesta HTTP corrente.

Se si fa clic sull'icona di Doctrine 2 ti verrànno presentate tutte le informazioni riguardanti le query che sono state eseguite da Doctrine 2 sulla base dei dati per la richiesta HTTP corrente

Come si può vedere nella schermata qui sopra, ci sono un certo numero di query eseguite per una richiesta alla homepage. La seconda query eseguita recupera le entità blog dal database e viene eseguito come conseguenza del metodo getLatestBlogs () sula classe BlogRepository. A seguito di questa query si noterà un numero di query che vanno ad osservare il database, un blog alla volta. Possiamo vedere questo a causa di WHERE t0.blog_id = ? in ogniuna delle queries, dove il ? è replicato per i valori del parametro ( blog Id) . Ogniuna di queste query sono il risultato di una chiamata a {{ blog.comments }} nel template homepage . Questa funzione è esecutata ogni volta da, Doctrine 2 che carica le entità Comment dalle relative entità Blog.
Mentre lazy loading è molto efficace per il recupero di entità correlate dal database, non è sempre il modo più efficace per realizzare questo compito. Doctrine 2 offre la possibilità di unire le entità correlate insieme per l'interrogazione del database. In questo modo possiamo recuperare l'entità blog e relative Commento dal database in una sola query. Aggiornare il codice QueryBuilder nel BlogRepository in src / Blogger / BlogBundle / Repository / BlogRepository.php facendo un join su i commenti.ts.
// src/Blogger/BlogBundle/Repository/BlogRepository.php
public function getLatestBlogs($limit = null)
{
$qb = $this->createQueryBuilder('b')
->select('b, c')
->leftJoin('b.comments', 'c')
->addOrderBy('b.created', 'DESC');
if (false === is_null($limit))
$qb->setMaxResults($limit);
return $qb->getQuery()
->getResult();
}
Se ora si aggiorna la homepage ed esaminiamo le uscite di Doctrine 2 nella barra degli strumenti di sviluppo si noterà il numero di queries è diminuito. È inoltre possibile consultare la tabella commento è stata unita alla tabella blog.
Lazy loading and joining i due concetti correlati sono molto potenti, ma hanno bisogno di essere usati correttamente. Il giusto equilibrio tra i 2 deve essere trovato per assicurare che l'applicazione venga eseguita nel modo più efficiente possibile. In un primo momento potrebbe sembrare un'ottima opportunità di fare un collegamento per ogni entità , quindi non sarà necessario caricare lazy e il numero di query di database rimarrà sempre bassa. Tuttavia, è importante ricordare che più informazioni si recupera dal database,più elaborazione deve essere fatta da Doctrine 2per riportare questo negli oggetti entità. Più dati significa anche maggiore quantità di memoria che viene utilizzato dal server per memorizzare gli oggetti entità.
. Aggiornare il template homepage in src / Blogger / BlogBundle / Resources / views / Page / index.html.twig per aggiungere un link per visualizzare i commenti del blog.
{# src/Blogger/BlogBundle/Resources/views/Page/index.html.twig #}
{# .. #}
<footer class="meta">
<p>Comments: <a href="{{ path('BloggerBlogBundle_blog_show', { 'id': blog.id }) }}#comments">{{ blog.comments|length }}</a></p>
<p>Posted by <span class="highlight">{{ blog.author }}</span> at {{ blog.created|date('h:iA') }}</p>
<p>Tags: <span class="highlight">{{ blog.tags }}</span></p>
</footer>
{# .. #}
La Sidebar ¶
Attualmente la barra laterale di symblog è un po 'vuota. Provvederemo ad aggiornare questa con 2 componenti comuni, ai blog la tag cloud e una lista degli ultimi commenti.
Tag Cloud¶
La Tag Cloud mostra i tag più pertinenti per ogni blog in un modo che consente di visualizzare i tag più comuni e più in vista . Per raggiungere questo obiettivo abbiamo bisogno di un modo per recuperare tutti i tag per tutti i blog. Dobbiamo creare alcuni nuovi metodi nella classe BlogRepository per fare questo. Aggiornare la classe BlogRepository in src / Blogger / BlogBundle / Repository / BlogRepository.php con il seguente codice .
// src/Blogger/BlogBundle/Repository/BlogRepository.php
public function getTags()
{
$blogTags = $this->createQueryBuilder('b')
->select('b.tags')
->getQuery()
->getResult();
$tags = array();
foreach ($blogTags as $blogTag)
{
$tags = array_merge(explode(",", $blogTag['tags']), $tags);
}
foreach ($tags as &$tag)
{
$tag = trim($tag);
}
return $tags;
}
public function getTagWeights($tags)
{
$tagWeights = array();
if (empty($tags))
return $tagWeights;
foreach ($tags as $tag)
{
$tagWeights[$tag] = (isset($tagWeights[$tag])) ? $tagWeights[$tag] + 1 : 1;
}
// Shuffle the tags
uksort($tagWeights, function() {
return rand() > rand();
});
$max = max($tagWeights);
// Max of 5 weights
$multiplier = ($max > 5) ? 5 / $max : 1;
foreach ($tagWeights as &$tag)
{
$tag = ceil($tag * $multiplier);
}
return $tagWeights;
}
Poiché i tag vengono memorizzati nel database come valori separati da virgola (CSV) abbiamo bisogno di un modo per dividerli e restituirli come un array. Ciò è ottenuto dal metodo getTags (). Il metodo getTagWeights() è quindi in grado di utilizzare una matrice di tag per calcolare il peso di ciascuna etichetta in base alla sua popolarità all'interno della matrice. I tag sono mescolati in modo random per la visualizzazione della pagina.
Ora siamo in grado di generare il tag cloud, abbiamo bisogno di visualizzarla. Creare una nuova azione nella PageController in src / Blogger / BlogBundle / Controller / PageController.php per gestire la barra laterale.
// src/Blogger/BlogBundle/Controller/PageController.php
public function sidebarAction()
{
$em = $this->getDoctrine()
->getEntityManager();
$tags = $em->getRepository('BloggerBlogBundle:Blog')
->getTags();
$tagWeights = $em->getRepository('BloggerBlogBundle:Blog')
->getTagWeights($tags);
return $this->render('BloggerBlogBundle:Page:sidebar.html.twig', array(
'tags' => $tagWeights
));
}
L'azione è molto semplice, utilizza i 2 nuovi metodi BlogRepository per generare il tag cloud, e passa questo alla vista. Ora andiamo a creare questa vista situata in src / Blogger / BlogBundle / Resources / views / Page / sidebar.html.twig .
{# src/Blogger/BlogBundle/Resources/views/Page/sidebar.html.twig #}
<section class="section">
<header>
<h3>Tag Cloud</h3>
</header>
<p class="tags">
{% for tag, weight in tags %}
<span class="weight-{{ weight }}">{{ tag }}</span>
{% else %}
<p>There are no tags</p>
{% endfor %}
</p>
</section>
Il template è anche molto semplice.Si itera poco più dei vari tags e una classe si occupa del peso del tag. Il ciclo for ci introduce per accedere alle chiavi e al valore delle coppie, dove tag inizia la chiave e weight il valore. Ci sono un certo numero di varianti di come utilizzare il ciclo for fornite nella Twig documentazione .
Se si guarda indietro al template principale di layout BloggerBlogBundle in src / Blogger / BlogBundle / Resources / views / layout.html.twig noterete che abbiamo messo un segnaposto per il blocco nella sidebar.Possiamo ora i sostituire questo rendendo la nuova azione sidebar. Ricordate dal capitolo precedente che il metodo Twig render rende i contenuti da una azione di controllo, in questo caso l' azione sidebar della pagina controller.
{# src/Blogger/BlogBundle/Resources/views/layout.html.twig #}
{# .. #}
{% block sidebar %}
{% render "BloggerBlogBundle:Page:sidebar" %}
{% endblock %}
Infine, aggiungiamo il CSS per il tag cloud. Aggiungere un nuovo foglio di stile in src / Blogger / BlogBundle / Resources / public / css / sidebar.css .
.sidebar .section { margin-bottom: 20px; }
.sidebar h3 { line-height: 1.2em; font-size: 20px; margin-bottom: 10px; font-weight: normal; background: #eee; padding: 5px; }
.sidebar p { line-height: 1.5em; margin-bottom: 20px; }
.sidebar ul { list-style: none }
.sidebar ul li { line-height: 1.5em }
.sidebar .small { font-size: 12px; }
.sidebar .comment p { margin-bottom: 5px; }
.sidebar .comment { margin-bottom: 10px; padding-bottom: 10px; }
.sidebar .tags { font-weight: bold; }
.sidebar .tags span { color: #000; font-size: 12px; }
.sidebar .tags .weight-1 { font-size: 12px; }
.sidebar .tags .weight-2 { font-size: 15px; }
.sidebar .tags .weight-3 { font-size: 18px; }
.sidebar .tags .weight-4 { font-size: 21px; }
.sidebar .tags .weight-5 { font-size: 24px; }
Infine, aggiungiamo il CSS per il tag cloud. Aggiungere un nuovo foglio di stile in src / Blogger / BlogBundle / Resources / public / css / sidebar.css
{# src/Blogger/BlogBundle/Resources/views/layout.html.twig #}
{# .. #}
{% block stylesheets %}
{{ parent() }}
<link href="{{ asset('bundles/bloggerblog/css/blog.css') }}" type="text/css" rel="stylesheet" />
<link href="{{ asset('bundles/bloggerblog/css/sidebar.css') }}" type="text/css" rel="stylesheet" />
{% endblock %}
{# .. #}
Nota
Se non si sta utilizza il metodo di collegamento simbolico per fare riferimento alle attività del bundle nella cartella web è necessario eseguire nuovamente il task asset per copiare il nuovo file CSS
$ php app/console assets:install web
Se ora si aggiorna il sito symblog vedrete la tag cloud nella barra laterale. Al fine di ottenere i tag con pesi diversi, potrebbe essere necessario aggiornare le fixture blog così alcuni tag vengono visualizzati più di altri..
Commenti recenti ¶
Ora il Tag Cloud è a posto, andiamo ad aggiungere l'ultimo componente i Commenti alla barra laterale.
In primo luogo abbiamo bisogno di un modo per recuperare gli ultimi commenti per i blog. Per fare questo si aggiunge un nuovo metodo al CommentRepository in src / Blogger / BlogBundle / Repository / CommentRepository.php .
<?php
// src/Blogger/BlogBundle/Repository/CommentRepository.php
public function getLatestComments($limit = 10)
{
$qb = $this->createQueryBuilder('c')
->select('c')
->addOrderBy('c.id', 'DESC');
if (false === is_null($limit))
$qb->setMaxResults($limit);
return $qb->getQuery()
->getResult();
}
Prossimo aggiornamento per l'azione sidebar in src / Blogger / BlogBundle / Controller / PageController.php per recuperare gli ultimi commenti e passarli alla vista.
// src/Blogger/BlogBundle/Controller/PageController.php
public function sidebarAction()
{
// ..
$commentLimit = $this->container
->getParameter('blogger_blog.comments.latest_comment_limit');
$latestComments = $em->getRepository('BloggerBlogBundle:Comment')
->getLatestComments($commentLimit);
return $this->render('BloggerBlogBundle:Page:sidebar.html.twig', array(
'latestComments' => $latestComments,
'tags' => $tagWeights
));
}
Noterete che abbiamo usato un nuovo parametro chiamato blogger_blog.comments.latest_comment_limit per limitare il numero di commenti recuperati. Per creare questo parametro aggiornare il file config in src / Blogger / BlogBundle / Resources / config / config.yml con il seguente.
# src/Blogger/BlogBundle/Resources/config/config.yml
parameters:
# ..
# Blogger max latest comments
blogger_blog.comments.latest_comment_limit: 10
Infine abbiamo bisogno di rendere gli ultimi commenti nel template sidebar. Aggiornare il template in src / Blogger / BlogBundle / Resources / views / Page / sidebar.html.twig con il seguente.
{# src/Blogger/BlogBundle/Resources/views/Page/sidebar.html.twig #}
{# .. #}
<section class="section">
<header>
<h3>Latest Comments</h3>
</header>
{% for comment in latestComments %}
<article class="comment">
<header>
<p class="small"><span class="highlight">{{ comment.user }}</span> commented on
<a href="{{ path('BloggerBlogBundle_blog_show', { 'id': comment.blog.id }) }}#comment-{{ comment.id }}">
{{ comment.blog.title }}
</a>
[<em><time datetime="{{ comment.created|date('c') }}">{{ comment.created|date('Y-m-d h:iA') }}</time></em>]
</p>
</header>
<p>{{ comment.comment }}</p>
</p>
</article>
{% else %}
<p>There are no recent comments</p>
{% endfor %}
</section>
Se ora si aggiorna il sito symblog potrete vedere gli ultimi commenti visualizzati nella barra laterale sotto la Tag Cloud.

Twig Extensions¶
Finora abbiamo pubblicato la visualizzazione della data per i commenti del blog in un formato data standard quale 2011/04/21 . Un approccio molto più bello sarebbe quello di visualizzare le date di commento, in termini di quanto tempo fa il commento è stato pubblicato, come inviato 3 ore fa . Potremmo aggiungere un metodo alle entità Commento per raggiungere questo obiettivo e modificare i template per usare questo metodo al posto di {{ comment.created | date ('Ymd h: iA ') }} .
Si può decidere di utilizzare questa metodo altrimenti sarebbe più sensato spostare questo fuori dall'entità Comment. Trasformare la data è un compito specifico della vista, dobbiamo attuare questa operazione utilizzando il motore di template Twig. che ha questa capacità, fornendo un'interfaccia Extension.
Siamo in grado di utilizzare l' estensione interfaccia di Twig estendendo le funzionalità predefinite che fornisce. Stiamo per creare un nuovo filtro Twig che può essere utilizzato come segue.
{{ comment.created|created_ago }}
Questo farà tornare la data del commento in un formato come pubblicato 2 giorni fa .
L'estensione ¶
Creare un file per l'estensione Twig in src / Blogger / BlogBundle / Twig / Extensions / BloggerBlogExtension.php e aggiornate con il seguente contenuto.
<?php
// src/Blogger/BlogBundle/Twig/Extensions/BloggerBlogExtension.php
namespace Blogger\BlogBundle\Twig\Extensions;
class BloggerBlogExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
'created_ago' => new \Twig_Filter_Method($this, 'createdAgo'),
);
}
public function createdAgo(\DateTime $dateTime)
{
$delta = time() - $dateTime->getTimestamp();
if ($delta < 0)
throw new \InvalidArgumentException("createdAgo is unable to handle dates in the future");
$duration = "";
if ($delta < 60)
{
// Seconds
$time = $delta;
$duration = $time . " second" . (($time > 1) ? "s" : "") . " ago";
}
else if ($delta <= 3600)
{
// Mins
$time = floor($delta / 60);
$duration = $time . " minute" . (($time > 1) ? "s" : "") . " ago";
}
else if ($delta <= 86400)
{
// Hours
$time = floor($delta / 3600);
$duration = $time . " hour" . (($time > 1) ? "s" : "") . " ago";
}
else
{
// Days
$time = floor($delta / 86400);
$duration = $time . " day" . (($time > 1) ? "s" : "") . " ago";
}
return $duration;
}
public function getName()
{
return 'blogger_blog_extension';
}
}
Creazione l'estensione è abbastanza semplice. Abbiamo sovrascritto getFilters () per restituire dei i filtri che vogliamo siano disponibili. In questo caso abbiamo creato il filtro created_ago. Questo filtro usa il metodo createdAgo , che trasforma semplicemente un oggetto DateTime in una stringa che riporta il valore del tempo trascorso memorizzato nell'oggetto DateTime.
Qui in un formato inglese per averlo in italiano apportare le varie modifiche alle diciture.
Registrazione del Extension ¶
Per avere a disposizione un estensione Twig abbiamo bisogno di aggiornare il file dei servizi in src / Blogger / BlogBundle / Resources / config / services.yml con il seguente.
services:
blogger_blog.twig.extension:
class: Blogger\BlogBundle\Twig\Extensions\BloggerBlogExtension
tags:
- { name: twig.extension }
Potete vedere questo registrando il nuovo servizio alla classe BloggerBlogExtension dell'estensione Twig che abbiamo appena creato.
Aggiornamento della vista ¶
Il nuovo Twig filtro è ora pronto per essere utilizzato. Ci consente di aggiornare l'elenco aggiornato della sidebar commenti e di utilizzare il filtro created_ago Aggiornare il template di sidebar in src / Blogger / BlogBundle / Resources / views / Page / sidebar.html.twig con il seguente.
{# src/Blogger/BlogBundle/Resources/views/Page/sidebar.html.twig #}
{# .. #}
<section class="section">
<header>
<h3>Latest Comments</h3>
</header>
{% for comment in latestComments %}
{# .. #}
<em><time datetime="{{ comment.created|date('c') }}">{{ comment.created|created_ago }}</time></em>
{# .. #}
{% endfor %}
</section>
Se ora puntate il browser a http://symblog.dev/app_dev.php/ potrete vedere le date dei commenti aggiornate che utilizzano il filtro di Twig per rendere il tempo a ora dal momento che il commento è stato pubblicato.
Andiamo ad aggiornare anche i commenti elencati nella pagina show blog per utilizzare il nuovo filtro. Sostituire il contenuto del template in src / Blogger / BlogBundle / Resources / views / commento / index.html.twig con il seguente.
{# src/Blogger/BlogBundle/Resources/views/Comment/index.html.twig #}
{% for comment in comments %}
<article class="comment {{ cycle(['odd', 'even'], loop.index0) }}" id="comment-{{ comment.id }}">
<header>
<p><span class="highlight">{{ comment.user }}</span> commented <time datetime="{{ comment.created|date('c') }}">{{ comment.created|created_ago }}</time></p>
</header>
<p>{{ comment.comment }}</p>
</article>
{% else %}
<p>There are no comments for this post. Be the first to comment...</p>
{% endfor %}
Nota
Ci sono una serie di utili Twig estensioni disponibili tramite la libreria Twig Extensions su GitHub. Se si crea un estensione si può inviare una richiesta di pull per questo repository in modo che altre persone la possono utilizzare.
Slugifying l'URL ¶
Attualmente l'URL di ogni post del blog mostra solo l'Id blog. Mentre questo è perfettamente accettabile da un punto di vista funzionale, non è grande per il SEO. Ad esempio, l'URL http://symblog.dev/1 non fornisce alcuna informazione circa il contenuto del blog, qualcosa di simile http://symblog.dev/1/a-day-with-symfony2 sarebbe molto migliore. Per raggiungere questo obiettivo noi slugify il titolo del blog per poi usarlo come parte di questa URL. Slugifying il titolo sarà rimuovere tutti i caratteri non ASCII e sostituirli con un - .
Aggiornare il routing ¶
Per iniziare dobbiamo modificare la regola di routing per la mostra pagina blog per aggiungere il componente slug. Aggiornare la regola di routing in src / Blogger / BlogBundle / Resources / config / routing.yml
# src/Blogger/BlogBundle/Resources/config/routing.yml
BloggerBlogBundle_blog_show:
pattern: /{id}/{slug}
defaults: { _controller: BloggerBlogBundle:Blog:show }
requirements:
_method: GET
id: \d+
Il controller ¶
Come con l'attuale componente id , il nuovo componente slug verrà passato alla azione di controllo come argomento, aggiornare il controllore in src / Blogger / BlogBundle / Controller / BlogController.php per riflettere questo.
// src/Blogger/BlogBundle/Controller/BlogController.php
public function showAction($id, $slug)
{
// ..
}
Nota
L'ordine in cui vengono passati gli argomenti nell' azione di controllo non importa, solo con il nome . Symfony2 è in grado di abbinare gli argomenti di routing con la lista dei parametri . Anche se non abbiamo ancora utilizzato la componente di default per i valori menzionati qui. Se abbiamo aggiunto un altro componente sulla regola di routing si può specificare un valore predefinito per il componente come opzione di default.
BloggerBlogBundle_blog_show:
pattern: /{id}/{slug}/{comments}
defaults: { _controller: BloggerBlogBundle:Blog:show, comments: true }
requirements:
_method: GET
id: \d+
public function showAction($id, $slug, $comments)
{
// ..
}
Utilizzando questo metodo, di richiesta http://symblog.dev/1/symfony2-blog comporterebbe che la variabile $ comments debba essere impostata su true nella showAction .
Slugifying del titolo ¶
Sicome si desidera generare lo slug dal titolo del blog, verra automaticamente generato il valore slug. Potremmo semplicemente eseguire questa operazione in fase di esecuzione sul campo del titolo, ma invece si memorizzerà lo slug nelle entità Blog per persisterlo nel database.
Aggiornare l'entità Blog ¶
Andiamo ad aggiungere un nuovo membro alle entità Blog per memorizzare lo slug. Aggiornare l'entità Blog in src / Blogger / BlogBundle / Entità / Blog.php
// src/Blogger/BlogBundle/Entity/Blog.php
class Blog
{
// ..
/**
* @ORM\Column(type="string")
*/
protected $slug;
// ..
}
Ora generare le funzioni di accesso per il nuovo membro $ slug. Come prima eseguire il seguente comando.
$ php app/console doctrine:generate:entities Blogger
Successivamente aggiornate lo schema del database.
$ php app/console doctrine:migrations:diff
$ php app/console doctrine:migrations:migrate
Per generare il valore di slug useremo il metodo slugify dal 1 tutorial symfony Jobeet. Aggiungere il metodo slugify alle entità Blog in src / Blogger / BlogBundle / Entità / Blog.php
// src/Blogger/BlogBundle/Entity/Blog.php
public function slugify($text)
{
// replace non letter or digits by -
$text = preg_replace('#[^\\pL\d]+#u', '-', $text);
// trim
$text = trim($text, '-');
// transliterate
if (function_exists('iconv'))
{
$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
}
// lowercase
$text = strtolower($text);
// remove unwanted characters
$text = preg_replace('#[^-\w]+#', '', $text);
if (empty($text))
{
return 'n-a';
}
return $text;
}
Sicome si desidera generare automaticamente lo slug dal titolo saremo in grado di generare lo slug quando il valore del titolo è impostato. Per questo andiamo ad aggiornare il setTitle di accesso per impostare anche il valore dello slug. Aggiornare l'entità Blog in src / Blogger / BlogBundle / Entità / Blog.php con il seguente.
// src/Blogger/BlogBundle/Entity/Blog.php
public function setTitle($title)
{
$this->title = $title;
$this->setSlug($this->title);
}
Successivamente aggiorneremo il metodo setSlug per slugify prima che lo slug venga impostato.
// src/Blogger/BlogBundle/Entity/Blog.php
public function setSlug($slug)
{
$this->slug = $this->slugify($slug);
}
Ora ricarichiamo le fixture di dati per generare lo slug blog.$ php app/console doctrine:fixtures:load
Aggiornamento delle rotte generate ¶
Infine abbiamo bisogno di aggiornare le chiamate esistenti per la generazione di percorsi alla pagina di show blog. Ci sono un certo numero di posizioni tutte queste devono essere aggiornate.
Aprire il template home page in src / Blogger / BlogBundle / Resources / views / Page / index.html.twig e sostituirne il contenuto con il seguente. Ci sono 3 modifiche alla generazione del percorso BloggerBlogBundle_blog_show in questo template . Le modifiche sono semplicemente passare slug blog per la funzione di Twig path
{# src/Blogger/BlogBundle/Resources/views/Page/index.html.twig #}
{% extends 'BloggerBlogBundle::layout.html.twig' %}
{% block body %}
{% for blog in blogs %}
<article class="blog">
<div class="date"><time datetime="{{ blog.created|date('c') }}">{{ blog.created|date('l, F j, Y') }}</time></div>
<header>
<h2><a href="{{ path('BloggerBlogBundle_blog_show', { 'id': blog.id, 'slug': blog.slug }) }}">{{ blog.title }}</a></h2>
</header>
<img src="{{ asset(['images/', blog.image]|join) }}" />
<div class="snippet">
<p>{{ blog.blog(500) }}</p>
<p class="continue"><a href="{{ path('BloggerBlogBundle_blog_show', { 'id': blog.id, 'slug': blog.slug }) }}">Continue reading...</a></p>
</div>
<footer class="meta">
<p>Comments: <a href="{{ path('BloggerBlogBundle_blog_show', { 'id': blog.id, 'slug': blog.slug }) }}#comments">{{ blog.comments|length }}</a></p>
<p>Posted by <span class="highlight">{{ blog.author }}</span> at {{ blog.created|date('h:iA') }}</p>
<p>Tags: <span class="highlight">{{ blog.tags }}</span></p>
</footer>
</article>
{% else %}
<p>There are no blog entries for symblog</p>
{% endfor %}
{% endblock %}
Inoltre, un aggiornamento deve essere fatto per l'ultima sezione Commenti del template sidebar in src / Blogger / BlogBundle / Resources / views / Page / sidebar.html.twig .
{# src/Blogger/BlogBundle/Resources/views/Page/sidebar.html.twig #}
{# .. #}
<a href="{{ path('BloggerBlogBundle_blog_show', { 'id': comment.blog.id, 'slug': comment.blog.slug }) }}#comment-{{ comment.id }}">
{{ comment.blog.title }}
</a>
{# .. #}
Infine il createAction del CommentController deve essere aggiornato quando si reindirizza alla pagina show blog su un commento postato con successo. Aggiornare il CommentController in src / Blogger / BlogBundle / Controller / CommentController.php con le seguenti.
// src/Blogger/BlogBundle/Controller/CommentController.php
public function createAction($blog_id)
{
// ..
if ($form->isValid()) {
// ..
return $this->redirect($this->generateUrl('BloggerBlogBundle_blog_show', array(
'id' => $comment->getBlog()->getId(),
'slug' => $comment->getBlog()->getSlug())) .
'#comment-' . $comment->getId()
);
}
// ..
}
Ora, se si passa alla homepage symblog a http://symblog.dev/app_dev.php/ e facciamo clik su uno dei titoli del blog vedrete che lo slug blog è stato aggiunto alla fine dell'URL.
Ambienti ¶
Gli ambienti è una funzionalità molto potente, ma semplice fornita da Symfony2. Potreste non essere a conoscenza, ma sono stati utilizzati gli ambienti fin dal punto 1 di questo tutorial. Con gli ambienti possiamo configurare vari aspetti di Symfony2 di come eseguire l'applicazione in modo diverso a seconda delle esigenze specifiche durante il ciclo di vita delle applicazioni. Per impostazione predefinita Symfony2 viene configurato con 3 ambienti:
- dev - Sviluppo
- Test - Test
- prod - Produzione
Lo scopo di questi ambienti è di per sé esplicativo, ma per quanto riguarda questi ambienti come sono stati configurati in modo diverso per le loro esigenze individuali. Nello sviluppare l' applicazione è utile avere la barra degli strumenti di sviluppo sullo schermo con le eccezioni e gli errori descrittivi per essere visualizzati, mentre nella produzione non si vuole nulla di tutto questo. In effetti, avere queste informazioni visualizzate sarebbe un rischio per la sicurezza perchè un sacco di dettagli riguardanti l'interno dell'applicazione e del server sarebbero esposti. Nella produzione sarebbe meglio visualizzare le pagine di errore personalizzate con messaggi semplificati, mettere la registrazione di queste informazioni in file di testo. Sarebbe anche utile avere lo strato di memorizzazione nella cache attivata per garantire che l'applicazione venga eseguita al meglio. Avere lo strato di caching abilitato nel ambiente di sviluppo sarebbe un critico perchè si avrebbe bisogno di svuotare la cache ogni volta che sono state apportate modifiche ai file di configurazione, ecc
Il test ambiente. Questo viene utilizzato quando si esegue il test dell'applicazione come unità o test funzionale. Non abbiamo ancora parlato di test, ma vi assicuro che sarà fatto in modo approfondito nei prossimi capitoli.
Front controller ¶
Finora in questo tutorial abbiamo utilizzato un unico ambiente sviluppo. utilizzando app_dev.php front controller al momento della richiesta di symblog, ad esempio http://symblog.dev/app_dev.php/about . Se diamo uno sguardo al front controller in web / app_dev.php vedrete la riga seguente:
$kernel = new AppKernel('dev', true);
Questa linea è quello che inizia Symfony2 per andare avanti. Qui è 'creata una nuova istanza della Symfony2 AppKernel che imposta l'ambiente dev .
Al contrario, se guardiamo al front controller per l'ambiente di produzione situato al web / app.php vediam
$kernel = new AppKernel('prod', false);
L'ambiente di test non dovrebbe essere eseguito tramite il browser web ed è per questo non vi è alcuna app_test.php front controller.
Impostazioni di configurazione ¶
Abbiamo visto sopra come i front controller vengono utilizzati per modificare l'ambiente,di come l'applicazione viene eseguita . Ora vedremo come le varie impostazioni vengono modificate durante l'esecuzione in ogni ambiente. Se si da uno sguardo ai file in app / config vedrai un certo numero di file config.yml. In particolare vi è uno principale, denominato config.yml e tutti gli altri 3 con suffisso con il nome di un ambiente; config_dev.yml , config_test.yml e config_prod.yml . Ognuno di questi file viene caricato a seconda del contesto attuale. Se esplorate il config_dev.yml si vedranno le seguenti righe nella parte superiore.
imports:
- { resource: config.yml }
La direttiva import caricherà il file config.yml. La stessa import direttiva può essere trovata nella parte superiore dei file d'ambiente di configurazione, config_test.yml e config_prod.yml . Con l'inclusione di un insieme comune di impostazioni di configurazione definite in config.yml siamo in grado di ignorare le impostazioni specifiche per ogni ambiente. Possiamo vedere nella file config di sviluppo in app / config / config_dev.yml le seguenti righe configurano l'utilizzo della barra degli strumenti di sviluppo.
# app/config/config_dev.yml
web_profiler:
toolbar: true
Questa impostazione è assente dal file di config di produzione , siccome non vogliamo in questo ambiente che la barra degli strumenti venga visualizzataEsecuzione in produzione ¶
Per quelli di voi ansiosi di vedere il tuo sito in esecuzione nell'ambiente di produzione ora è il momento.
In primo luogo abbiamo bisogno di cancellare la cache utilizzando uno dei task di Symfony2 .
$ php app/console cache:clear --env=prod
Ora puntate il browser su http://symblog.dev/ . Si noti la mancanza di app_dev.php front controller .
Nota
Per quelli di voi con la configurazione Dynamic degli host virtuali come mostrato nella prima parte, è necessario aggiungere quanto segue al file. Htaccess in web / .htaccess .
<IfModule mod_rewrite.c>
RewriteBase /
# ..
</IfModule>
Si noterà che il sito sembra più o meno lo stesso, ma alcune caratteristiche importanti sono diversi. La barra degli strumenti di sviluppo è ormai sparita e il messaggio di eccezione dettagliati non vengono più visualizzati, provare ad andare ahttp://symblog.dev/999.

Il messaggio d'errore dettagliato è sostituito da un messaggio semplificato che informa l'utente del problema. Questa schermata di eccezione può essere personalizzata in base al look and feel dell'applicazione. Esploreremo questo nei capitoli successivi.
Inoltre si noterà il file app / logs / prod.log si sta riempiendo con i registri in materia di esecuzione dell'applicazione. Questo è un utile punto di chiamata quando si hanno problemi con l'applicazione in produzione come errori e ecc
.
Nota
Come la richiesta a http://symblog.dev/ finisce per chiamare la rotta attraverso il file app.php? Sono sicuro che tutti utilizzano index.html e index.php per la creazione di file che agiscono come l'indice di siti, ma come app.php può sostituire questo. Questo grazie ad una RewriteRule nel file web / .htaccess
RewriteRule ^(.*)$ app.php [QSA,L]
Possiamo vedere che questa linea ha una espressione regolare che corrisponde a qualsiasi testo, indicato con ^ (. *) $ e passa questo app.php .
Mentre sono stati affrontati i principi fondamentali dell'ambiente di produzione, non abbiamo coperto molte altre attività connesse, quali personalizzazione delle pagine di errore e di distribuzione su server di produzione utilizzando strumenti come capifony . Questi argomenti verranno trattati nei capitoli successivi.
Creazione di nuovi ambienti ¶
Infine vale la pena notare che è possibile impostare i propri ambienti facilmente in Symfony2. Ad esempio, è possibile un ambiente di staging che sarà eseguito sul server di produzione, ma la produzione con alcune delle informazioni di debug, come eccezioni. Ciò consentirebbe di testare la piattaforma manualmente sul server di produzione effettivo, siccome le configurazioni di produzione e sviluppo di server possono differire.
La creazione di un nuovo ambiente è un compito semplice, ma non rientra nell'ambito di questo tutorial. C'è un ottimo articolo nel libro di Symfony2 che si occupa di questo.
Assetic¶
Symfony2 distribuzione standard viene fornito con una libreria per la gestione di asset chiamata Assetic . La libreria è stato sviluppato da Kris Wallsmith ed è stato ispirato dalla libreria Python webassets .
Offre Assetic con 2 parti di asset management, le attività come immagini, fogli di stile e JavaScript e dei filtri che possono essere applicati a tali attività. Questi filtri sono in grado di svolgere compiti utili quali minifying CSS e JavaScript, passando file CoffeeScript attraverso il compilatore CoffeeScript, e combinare file di asset insieme per ridurre il numero di richieste HTTP al server.
Attualmente abbiamo usato la funzione Twig asset per includere attività nel template come segue.
<link href="{{ asset('bundles/bloggerblog/css/blog.css') }}" type="text/css" rel="stylesheet" />
La chiamata alla funzione asset sarà sostituita per Assetic.
Assets¶
La libreria Assetic descrive una risorsa come segue:
Un'attività Assetic è qualcosa con contenuti filtrabile che può essere caricato e scaricato. Un'attività comprende anche i metadati, alcuni dei quali possono essere manipolati e alcune delle quali è immutabile.
In parole povere, le attività sono le risorse, che l'applicazione utilizza come ad esempio i fogli di stile e immagini.
Fogli di stile ¶
Iniziamo sostituendo le chiamate correnti per asset per i fogli di stile nel template principale di layout BloggerBlogBundleAggiornare il contenuto del template in src / Blogger / BlogBundle / Resources / views / layout.html.twig con il seguente
{# src/Blogger/BlogBundle/Resources/views/layout.html.twig #}
{# .. #}
{% block stylesheets %}
{{ parent () }}
{% stylesheets
'@BloggerBlogBundle/Resources/public/css/*'
%}
<link href="{{ asset_url }}" rel="stylesheet" media="screen" />
{% endstylesheets %}
{% endblock %}
{# .. #}
Abbiamo sostituito i due precedenti link per i file CSS con alcune funzionalità Assetic. Utilizzando i fogli di stile da Assetic abbiamo specificato che tutti i file CSS nel percorso src / Blogger / BlogBundle / Resources / public / css deve essere combinati in un file e poi liberati .L' Unione dei file è un modo molto semplice ma efficace per ottimizzare il vostro sito web frontend, riducendo il numero dei file necessari. Meno file significa meno richieste HTTP al server. Anche se abbiamo usato il * per specificare tutti i file nella css directory avremmo potuto semplicemente elencare ogni singolo file come segue.
{# src/Blogger/BlogBundle/Resources/views/layout.html.twig #}
{# .. #}
{% block stylesheets %}
{{ parent () }}
{% stylesheets
'@BloggerBlogBundle/Resources/public/css/blog.css'
'@BloggerBlogBundle/Resources/public/css/sidebar.css'
%}
<link href="{{ asset_url }}" rel="stylesheet" media="screen" />
{% endstylesheets %}
{% endblock %}
{# .. #}
Il risultato finale in entrambi i casi è lo stesso. La prima opzione con il * assicura che quando nuovi file CSS vengono aggiunti alla directory, essi saranno sempre inclusi nel file CSS combinato da Assetic. Questo può non essere la funzionalità desiderata per il tuo sito, in modo da utilizzare uno dei due metodi sopra per soddisfare le vostre esigenze.
Se si da uno sguardo a l'output HTML via http://symblog.dev/app_dev.php/ vedrete il CSS è stato inserito qualcosa di simile a questo (si noti che stiamo correndo nell'ambiente di sviluppo di nuovo).
<link href="/app_dev.php/css/d8f44a4_part_1_blog_1.css" rel="stylesheet" media="screen" />
<link href="/app_dev.php/css/d8f44a4_part_1_sidebar_2.css" rel="stylesheet" media="screen" />
In primo luogo vi state chiedendo perché ci sono 2 file. Sopra si affermava che Assetic avrebbe combinato i file in 1 file CSS. Questo perché siamo in esecuzione di symblog nell'ambiente di sviluppo. Possiamo chiedere Assetic per l'esecuzione in modalità non-debug impostando il flag di debug su false come segue.
{# src/Blogger/BlogBundle/Resources/views/layout.html.twig #}
{# .. #}
{% stylesheets
'@BloggerBlogBundle/Resources/public/css/*'
debug=false
%}
<link href="{{ asset_url }}" rel="stylesheet" media="screen" />
{% endstylesheets %}
{# .. #}
Ora, se si guarda il codice HTML rendering si vedrà qualcosa di simile.
<link href="/app_dev.php/css/3c7da45.css" rel="stylesheet" media="screen" />
Se si visualizzano i contenuti di questo file potrete vedere i 2 file CSS, blog.css e sidebar.css sono stati combinati in 1 file. Il nome dato al file generato CSS viene generato in modo casuale da Assetic. Se si desidera controllare il nome dato al file generato utilizzare l'opzione output come segue.
{% stylesheets
'@BloggerBlogBundle/Resources/public/css/*'
output='css/blogger.css'
%}
<link href="{{ asset_url }}" rel="stylesheet" media="screen" />
{% endstylesheets %}
Prima di continuare rimuovere il flag di debug dallo snippet di codice precedente siccome vogliamo riprendere il comportamento di default sul assets.
Abbiamo anche bisogno di aggiornare il template di base dell' applicazioni in / app / views / Resources base.html.twig .
{# app/Resources/views/base.html.twig #}
{# .. #}
{% block stylesheets %}
<link href='http://fonts.googleapis.com/css?family=Irish+Grover' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=La+Belle+Aurore' rel='stylesheet' type='text/css'>
{% stylesheets
'css/*'
%}
<link href="{{ asset_url }}" rel="stylesheet" media="screen" />
{% endstylesheets %}
{% endblock %}
{# .. #}
JavaScripts¶
Mentre attualmente non abbiamo alcun file Javascipt nella nostra applicazione, il suo uso in Assetic è molto simile a come usare i fogli di stile
{% javascripts
'@BloggerBlogBundle/Resources/public/js/*'
%}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}
Filtri ¶
Il vero potere in Assetic proviene dai filtri. I filtri possono essere applicati ad assets o collezioni di assets. Ci sono numerosi filtri previsti all'interno del nucleo della libreria compresi i seguenti filtri comuni:
- CssMinFilter: minifies CSS
- JpegoptimFilter: optimize your JPEGs
- Yui\CssCompressorFilter: compresses CSS using the YUI compressor
- Yui\JsCompressorFilter: compresses JavaScript using the YUI compressor
- CoffeeScriptFilter: compiles CoffeeScript into JavaScript
C'è una lista completa dei filtri disponibili nelAssetic Readme.
Molti di questi filtri passano il compito attuale su un altro programma o libreria, come YUI Compressor, quindi potrebbe essere necessario installare o configurare le librerie necessarie per utilizzare alcuni dei filtri.
Scarica YUI Compressor , estrarre l'archivio e copiare il file che si trova nella compilazione directory app/Resources/java/yuicompressor-2.4.6.jar . Questo presuppone che è stata scaricata la 2.4.6 versione di YUI Compressor. Se non cambia il numero di versione di conseguenza.
Successivamente, si configura un filtro Assetic al minify il CSS utilizzando il YUI Compressor. Aggiornare l'applicazione di configurazione in app / config / config.yml con il seguente.
# app/config/config.yml
# ..
assetic:
filters:
yui_css:
jar: %kernel.root_dir%/Resources/java/yuicompressor-2.4.6.jar
# ..
Abbiamo configurato un filtro chiamato yui_css che utilizzerà il YUI Compressor eseguibile Java l'abbiamo messo nella directory di risorse dell'applicazioni . Per poter utilizzare il filtro è necessario specificare per quali attività si desidera che il filtro venga applicato. Aggiornare il template in src / Blogger / BlogBundle / Resources / views / layout.html.twig per applicare il yui_css filtro.
{# src/Blogger/BlogBundle/Resources/views/layout.html.twig #}
{# .. #}
{% stylesheets
'@BloggerBlogBundle/Resources/public/css/*'
output='css/blogger.css'
filter='yui_css'
%}
<link href="{{ asset_url }}" rel="stylesheet" media="screen" />
{% endstylesheets %}
{# .. #}
Ora, se si aggiorna il sito symblog per visualizzare l'output dei file Assetic noterete che sono state ridotte La minimizzazione è grande per i server di produzione, ma può risultare per il debug difficile, specialmente quando si tratta di JavaScript. Siamo in grado di disattivare la minimizzazione durante l'esecuzione nell'ambiente sviluppo anteponendo il filtro con un ? come segue.
{% stylesheets
'@BloggerBlogBundle/Resources/public/css/*'
output='css/blogger.css'
filter='?yui_css'
%}
<link href="{{ asset_url }}" rel="stylesheet" media="screen" />
{% endstylesheets %}
Dumping the assets for production¶
Nella produzione siamo in grado di eseguire il dump dei file di asset utilizzando Assetic in modo che essi diventano risorse effettive sul disco pronto per essere servito dal server web. Il processo di creazione delle attività attraverso Assetic ad ogni richiesta di pagina può essere molto lenta, soprattutto quando i filtri vengono applicati alle attività.il Dumping per le attività di produzione garantisce che Assetic non venga utilizzato per servire i beni e invece i file pre-elaborati di asset sono serviti direttamente dal server web. Eseguire l'operazione seguente per creare il dump dei file di asset.
$ app/console --env=prod assetic:dump
Si noterà un certo numero di file CSS che sono stati creati in web / css compreso il combinato blogger.css. Ora, se eseguite li sito Web symblog nell'ambiente di produzione attraverso http://symblog.dev/ i file verranno serviti direttamente da questa cartella.
Nota
Se avete dump di asset per i file su disco e si desidera tornare nell'ambiente di sviluppo , sarà necessario ripulire i file di asset creati in web / per Assetic per consentire di ricrearli..
Letture aggiuntive ¶
Noi qui abbiamo usato solo la superficie di ciò che Assetic è in grado di eseguire. Ci sono molte risorse disponibili on-line in particolare nel libro di Symfony2 tra cui:
Come utilizzare Assetic per l'Asset Management
Come minify JavaScript e Fogli di stile con YUI Compressor
Come utilizzare Assetic per l'ottimizzazione dell'immagine con le funzioni di Twig
Come applicare un filtro Assetic ad una estensione del file specifico
Ci sono anche una serie di grandi articoli scritti da Richard Miller , tra cui:
Symfony2: Utilizzo CoffeeScript con Assetic
Symfony2: Assetic e Twig Funzioni
Nota
Vale la pena ricordare qui che Richard Miller ha una collezione di articoli eccellenti per quanto riguarda un certo numero di aree di Symfony2 sul suo sito tra Dependency Injection, Servizi e le guide di Assetic. Basta cercare i post con tag symfony2
Conclusione ¶
Abbiamo coperto un certo numero di nuove aree per quanto riguarda Symfony2 compresi gli ambienti Symfony2 e come utilizzare la libreria Assetic . Abbiamo anche fatto miglioramenti alla home page e aggiunto alcuni componenti per la sidebar.
Nel prossimo capitolo si passerà ai test. Esploreremo sia test unit e test funzionali con PHPUnit. Vedremo come Symfony2 viene fornito con una serie di classi per aiutare nella scrittura dei test funzionali che simulano le richieste web,che ci permettono di compilare form e fare clic su collegamenti e poi ispezionare la risposta restituita.