CREATING A SIMPLE WEBSITE USING SENSIOLABS (SYMFONY) SILEX AND TWIG
What is Silex?
Silex is a PHP Micro Framework from SensioLabs which based on Symfony components.
Getting Silex
To get started you will need to get Silex into your development environment. If you are using Vagrant, or you have Composer installed, you can just navigate to the folder you want to install Silex and create a composer.json file with the following inside.
{
"require": {
"silex/silex": "~1.1",
"twig/twig": ">=1.8,<2.0-dev",
"symfony/twig-bridge": "~2.3"
}
}
After that you just need to run:
composer install
If all that doesn’t make sense to you then you probably don’t want to use Composer, although I do recommend you check it out at some point, and instead want to download the archive from the Silex website. Please choose the Fat archive or you will be missing some of the dependencies required for this tutorial.
If you do download the archive, delete the web folder included with the archive. We will not be using that for this tutorial. DO NOT do that in a production environment. We are only doing that here to keep things simple as, depending on your setup, it can be complicated to keep Silex outside of the web root. We will look at how to do that in the second part of this tutorial.
{
"require": {
"silex/silex": "~1.1",
"twig/twig": ">=1.8,<2.0-dev",
"symfony/twig-bridge": "~2.3"
}
}
|
composer install
|
Silex Demo
Here the example on what we’ll do.
Including Silex
The first thing we need to do is create the one and only file that will contain PHP, the rest of the tutorial will be fleshing out the website with Twig template files and CSS. So in the folder you have extracted/installed Silex into create an index.php file.
I’m going to go over the file in a few steps to help explain what sections of the code do & so that it helps break things up. First up, let’s setup Silex.
<?php
require_once __DIR__.'/vendor/autoload.php';
$app = new Silex\Application();
$app['debug'] = true;
That’s all there is to it. This just includes the autoloader for Silex and creates a new application, stored into the $app variable.
Obviously you do not want to leave the debug flag in when running in production, but it provides a lot of detailed and useful information if we hit any errors during development. Just remember to comment it out or remove it when moving to a production environment.
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/views',
));
$app['twig'] = $app->share($app->extend('twig', function($twig, $app) {
$twig->addFunction(new \Twig_SimpleFunction('asset', function ($asset) use ($app) {
return sprintf('%s/%s', trim($app['request']->getBasePath()), ltrim($asset, '/'));
}));
return $twig;
}));
$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
Next up we register Twig with our app. Twig comes built into the Fat version of Silex as a service so we can just ask Silex to enable it and we are good to go. We also pass a path to our views folder. I have chosen to make a folder called views. Please feel free to change this to anything you like.
We are also adding a handy function to twig which will generate the URL to an asset, that would be any images, CSS files or JS files. This is useful because it saves space in our template files, and if we wanted to we could host assets on a CDN and we would only need to make a small change to our function. For those curious you would do this:
$app['twig'] = $app->share($app->extend('twig', function($twig, $app) {
$twig->addFunction(new \Twig_SimpleFunction('asset', function ($asset) use ($app) {
return sprintf('http://external.assets.com/%s', trim($app['request']->getBasePath()), ltrim($asset, '/'));
}));
return $twig;
}));
Simple and retroactive.
Finally we register the URL Service provided in the Silex framework so we can generate links without any complications.
$app->before(function ($request) use ($app) {
$app['twig']->addGlobal('active', $request->get("_route"));
});
This adds a global variable that will be available to all Twig templates which contains the name of the route that is being displayed. This can be useful for many things, but in this case I am going to use it to mark the current page’s navigation link as active. More on that later.
$app->before() is ran before the controller is executed and is ran regardless of the route being displayed. That’s why we are using it here, it is ran before the templating system has executed so we can add a global variable to Twig without any issues.
$app->get('/', function() use ($app) {
return $app['twig']->render('layout.twig');
})->bind('home');
$app->run();
Here we are starting to add the page routes. We’ll just go through the home page to start with, that way we don’t overload on information. For the moment we are just adding the index route and the run command.
This is pretty simple. We are creating a route for the path /. This would refer to the root of your domain, or if you were hosting from a sub-folder (like my demo) that would become the root. That is provided your Apache/Nginx config is set-up correctly, more on that in the tips at the end of this tutorial.
We simply pass the $app variable through to our function using the new use keyword introduced in PHP 5.3.0 (I believe). This allows us to use the Twig render and return it. Returning while in the context of a route will output to the browser. bind() gives the route a name so we can generate links to it using the URL Generator service later.
<?php
require_once __DIR__.'/vendor/autoload.php';
$app = new Silex\Application();
$app['debug'] = true;
|
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/views',
));
$app['twig'] = $app->share($app->extend('twig', function($twig, $app) {
$twig->addFunction(new \Twig_SimpleFunction('asset', function ($asset) use ($app) {
return sprintf('%s/%s', trim($app['request']->getBasePath()), ltrim($asset, '/'));
}));
return $twig;
}));
$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
|
$app['twig'] = $app->share($app->extend('twig', function($twig, $app) {
$twig->addFunction(new \Twig_SimpleFunction('asset', function ($asset) use ($app) {
return sprintf('http://external.assets.com/%s', trim($app['request']->getBasePath()), ltrim($asset, '/'));
}));
return $twig;
}));
|
$app->before(function ($request) use ($app) {
$app['twig']->addGlobal('active', $request->get("_route"));
});
|
$app->get('/', function() use ($app) {
return $app['twig']->render('layout.twig');
})->bind('home');
$app->run();
|
Introducing Twig
If you have never used Twig before prepare for a treat. Twig is a wonderful templating engine, I much prefer it to Smarty. It reminds me of Handlebars (a JS based templating engine) which is a good thing.
First, if you didn’t already, create a folder in your project root named the same as the name you gave the twig.path option when registering the Twig service with Silex. Remember? I called mine views. In that folder create a new file called layout.twig. Yes. twig is the file extension. You can use a normal HTML extension instead if you wish, but this extension helps remind me they are twig files.
In this file you need to create the layout or the common HTML code that all our pages will use. This is going to be a big code block, but here it is.
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>My Awesome Site</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="//maxcdn.bootstrapcdn.com/bootswatch/3.2.0/darkly/bootstrap.min.css" rel="stylesheet">
<link href="{{ asset('css/styles.css') }}" rel="stylesheet">
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{ path('home') }}">L&LLF</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li{% if active == 'home' %} class="active"{% endif %}><a href="{{ path('home') }}">Home</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
<div class="container">
{% block content %}
<div class="starter-template">
<p><img class="img-responsive" src="{{ asset('img/llama-loaf.jpg') }}" alt="Image 'Llama Loaf' by Ian Sane"></p>
<h1>Breeders. Shearers. Welsh.</h1>
<p class="lead">Come visit our luxurious Llama farm deep in the Welsh valleys.<br> See our magnificent Llama Spa where our Llamas chill before their big shows.</p>
<p><small>Image 'Llama Loaf' by <a href="https://www.flickr.com/photos/31246066@N04/" title="Ian Sane on Flickr">Ian Sane</a></small></p>
</div>
{% endblock %}
</div><!-- /.container -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</body>
</html>
That’s a lot of code, but the bulk of it is just a standard HTML document with Bootstrap attached via the BootstrapCDN.
First a quick note about Twig. In Twig using {{ }} tells Twig to echo the contained variable or the returned result of the enclosed function. Using {% %} tells Twig to expect logic (if…else etc) instead.
Let’s go through some of the unusual code. First up is our link tag.
<link href="{{ asset('css/styles.css') }}" rel="stylesheet">
You may remember we created a helper called asset in our Silex code. This is it in use. We just pass the path (relative to root) to our CSS file and the function converts it to a absolute URL. Remember this is just for local content or content on your CDN. If you are linking off-site just enter the full URL as you would normally.
Next up is the path() helper.
<a class="navbar-brand" href="{{ path('home') }}">L&LLF</a>
This produces a relative path (from root) to the route named home. Remember? We used the bind() method on our route to give it a name.
<li{% if active == 'home' %} class="active"{% endif %}><a href="{{ path('home') }}">Home</a></li>
Remember when we created our index.php file? We created a global variable for Twig called active this is why. That variable contains the currently active route, so we can use that to set the currently active nav button.
All we do is a quick if using Twig to check if the current link should be marked as active. Same applies for any other nav links added in the future, but with the route name changed. You could automate this by creating a service, but I wanted to keep it fairly simple & it isn’t much effort to copypasta then change three words.
Finally is the main chunk. The block.
{% block content %}
<div class="starter-template">
...
</div>
{% endblock %}
This creates a block that can then be overridden in any other Twig template that extends this one. More on that when we create our next page. The content inside will be shown if the template is displayed & no other template overrides that particular block. Because of that I have used it to display the home page content.
That’s all for the layout.twig file. Let’s move onto the CSS before we create any more pages.
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>My Awesome Site</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="//maxcdn.bootstrapcdn.com/bootswatch/3.2.0/darkly/bootstrap.min.css" rel="stylesheet">
<link href="{{ asset('css/styles.css') }}" rel="stylesheet">
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{ path('home') }}">L&LLF</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li{% if active == 'home' %} class="active"{% endif %}><a href="{{ path('home') }}">Home</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
<div class="container">
{% block content %}
<div class="starter-template">
<p><img class="img-responsive" src="{{ asset('img/llama-loaf.jpg') }}" alt="Image 'Llama Loaf' by Ian Sane"></p>
<h1>Breeders. Shearers. Welsh.</h1>
<p class="lead">Come visit our luxurious Llama farm deep in the Welsh valleys.<br> See our magnificent Llama Spa where our Llamas chill before their big shows.</p>
<p><small>Image 'Llama Loaf' by <a href="https://www.flickr.com/photos/31246066@N04/" title="Ian Sane on Flickr">Ian Sane</a></small></p>
</div>
{% endblock %}
</div><!-- /.container -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</body>
</html>
|
<link href="{{ asset('css/styles.css') }}" rel="stylesheet">
|
<a class="navbar-brand" href="{{ path('home') }}">L&LLF</a>
|
<li{% if active == 'home' %} class="active"{% endif %}><a href="{{ path('home') }}">Home</a></li>
|
{% block content %}
<div class="starter-template">
...
</div>
{% endblock %}
|
Minimal CSS
Create a folder called css in your project root & then create a file called styles.css. Inside all you need to replicate my demo site is this:
body {
padding-top: 50px;
}
.starter-template {
padding: 40px 15px;
text-align: center;
}
I wanted to keep the focus on building the site so it uses as much of Bootstrap’s default styling as possible. These are just two tweaks needed to add spacing in.
body {
padding-top: 50px;
}
.starter-template {
padding: 40px 15px;
text-align: center;
}
|
Taking Stock
Now seems like a good place to pause and look at what we have.
At this point you should have a home page built using Silex & Twig. You should be able to pull it open in your browser and see it in all its glory. Awesome, huh?
Adding About & Contact Pages
Now let’s add two more pages. First up we’ll add our About page. Let’s make our template first. Create a new folder inside your views folder called pages, inside that create a file called about.twig. While this isn’t necessary it does helps us keep our pages separated from our layout.
Place this inside the new about.twig file.
{% extends 'layout.twig' %}
{% block content %}
<div class="starter-template">
<p>
<img class="img-responsive" src="{{ asset('img/llama-farma.jpg') }}" alt="Llama Farmer? No problem!">
</p>
<p class="lead">Llama farmer? No problem, add your best llama pic above!</p>
</div>
<div class="row">
<div class="col-md-8">
<h2>Who Are We?</h2>
<p>We are Lily & Larry's Llamas. Llama farmers from Llangynidr in Wales.</p>
<h2>What Are Your Plans?</h2>
<p>To breed, show & shear award winning Llamas with bows in their fur.</p>
<h2>This Is Madness!</h2>
<p>That is because this is a demo, none of this content matters. It is all just nonsense.</p>
</div>
<div class="col-md-4">
<h3>Is This A Sidebar?</h3>
<p>Yes... Yes, it is. You could add more Llama based links here.</p>
</div>
</div>
{% endblock %}
This is pretty simple. The {% extends 'layout.twig' %} tells Twig that this file will be used in addition to our original layout.twig file. Now thanks to that we can re-define any blocks located in that file to insert new content. As you can see we redefine the content block here.
Now let’s create our contact template too. In the pages folder again, create contact.twig. Then fill it with this content.
{% extends 'layout.twig' %}
{% block content %}
<div class="starter-template">
<h1>Contact Us</h1>
<p class="lead">Want some Llama farming tips? Get in touch using the form below.</p>
</div>
<div class="row">
<div class="col-md-4">
<p><img class="img-responsive" src="{{ asset('img/long-llama.jpg') }}" alt="Image 'Llama' by Asra Valorshon"></p>
<p class="text-center"><small> Image 'Llama' by <a href="https://www.flickr.com/photos/26548797@N00/" title="Asra Valorshon on Flickr">Asra Valorshon</a></small></p>
</div>
<div class="col-md-8">
<p>Contact us using the form below and we'll get back in touch with you when we aren't shearing our luscious Llamas.</p>
<form role="form" method="get" action="{{ path('home') }}">
<div class="form-group">
<label for="name">Name</label>
<input type="name" class="form-control" id="name" placeholder="Enter Name">
</div>
<div class="form-group">
<label for="email">Email address</label>
<input type="email" class="form-control" id="email" placeholder="Enter email">
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea name="message" id="message" class="form-control" rows="3" placeholder="Enter Your Message"></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Send It</button>
</div>
</form>
</div>
</div>
{% endblock %}
There is nothing new in this file in regards to Silex or Twig. The contact form does not work in this tutorial, but I will be making a second part very soon showing you how to get it working.
Now one final change to a template. We need to add in two extra links to our Nav bar. Open up layout.twig and add these under the home link.
<li{% if active == 'about' %} class="active"{% endif %}><a href="{{ path('about') }}">About</a></li>
<li{% if active == 'contact' %} class="active"{% endif %}><a href="{{ path('contact') }}">Contact</a></li>
So you should now have:
<ul class="nav navbar-nav">
<li{% if active == 'home' %} class="active"{% endif %}><a href="{{ path('home') }}">Home</a></li>
<li{% if active == 'about' %} class="active"{% endif %}><a href="{{ path('about') }}">About</a></li>
<li{% if active == 'contact' %} class="active"{% endif %}><a href="{{ path('contact') }}">Contact</a></li>
</ul>
Now let’s add in the code to our index.php file so Silex will create the pages. Under your home route, add the following.
$app->get('/about', function() use ($app) {
return $app['twig']->render('pages/about.twig');
})->bind('about');
$app->get('/contact', function() use ($app) {
return $app['twig']->render('pages/contact.twig');
})->bind('contact');
That’s it. All done.
If you visit your project you should have an exact duplicate of the site shown in my demo. Well except for the images, if you swapped them out for kittens.
{% extends 'layout.twig' %}
{% block content %}
<div class="starter-template">
<p>
<img class="img-responsive" src="{{ asset('img/llama-farma.jpg') }}" alt="Llama Farmer? No problem!">
</p>
<p class="lead">Llama farmer? No problem, add your best llama pic above!</p>
</div>
<div class="row">
<div class="col-md-8">
<h2>Who Are We?</h2>
<p>We are Lily & Larry's Llamas. Llama farmers from Llangynidr in Wales.</p>
<h2>What Are Your Plans?</h2>
<p>To breed, show & shear award winning Llamas with bows in their fur.</p>
<h2>This Is Madness!</h2>
<p>That is because this is a demo, none of this content matters. It is all just nonsense.</p>
</div>
<div class="col-md-4">
<h3>Is This A Sidebar?</h3>
<p>Yes... Yes, it is. You could add more Llama based links here.</p>
</div>
</div>
{% endblock %}
|
{% extends 'layout.twig' %}
{% block content %}
<div class="starter-template">
<h1>Contact Us</h1>
<p class="lead">Want some Llama farming tips? Get in touch using the form below.</p>
</div>
<div class="row">
<div class="col-md-4">
<p><img class="img-responsive" src="{{ asset('img/long-llama.jpg') }}" alt="Image 'Llama' by Asra Valorshon"></p>
<p class="text-center"><small> Image 'Llama' by <a href="https://www.flickr.com/photos/26548797@N00/" title="Asra Valorshon on Flickr">Asra Valorshon</a></small></p>
</div>
<div class="col-md-8">
<p>Contact us using the form below and we'll get back in touch with you when we aren't shearing our luscious Llamas.</p>
<form role="form" method="get" action="{{ path('home') }}">
<div class="form-group">
<label for="name">Name</label>
<input type="name" class="form-control" id="name" placeholder="Enter Name">
</div>
<div class="form-group">
<label for="email">Email address</label>
<input type="email" class="form-control" id="email" placeholder="Enter email">
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea name="message" id="message" class="form-control" rows="3" placeholder="Enter Your Message"></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Send It</button>
</div>
</form>
</div>
</div>
{% endblock %}
|
<li{% if active == 'about' %} class="active"{% endif %}><a href="{{ path('about') }}">About</a></li>
<li{% if active == 'contact' %} class="active"{% endif %}><a href="{{ path('contact') }}">Contact</a></li>
|
<ul class="nav navbar-nav">
<li{% if active == 'home' %} class="active"{% endif %}><a href="{{ path('home') }}">Home</a></li>
<li{% if active == 'about' %} class="active"{% endif %}><a href="{{ path('about') }}">About</a></li>
<li{% if active == 'contact' %} class="active"{% endif %}><a href="{{ path('contact') }}">Contact</a></li>
</ul>
|
$app->get('/about', function() use ($app) {
return $app['twig']->render('pages/about.twig');
})->bind('about');
$app->get('/contact', function() use ($app) {
return $app['twig']->render('pages/contact.twig');
})->bind('contact');
|
Bahasa inggrise gawe google translate yo mas
Wkwkwkwkwkwkwkwkwkwk
But thanks for the information
That so cool