First Steps
This series is all about getting started as a complete beginner to the Symfony Framework. One of the hurdles that used to exist in getting started was in getting the framework code downloaded onto your local machine. You would need to download a zipped tarball file, extract it, and then run an installer script.
It was only with Symfony 2.1 onwards that Composer became a 'thing'. Yep, imagine that - using a framework like Symfony without a modern day package manager. Actually, Composer came about as a result of wanting a standard way to manage pulling in third party code (the famed Symfony bundles - which you may have heard about) into existing Symfony projects. Kinda crazy, right?
Anyway, fast-forward to today, and we don't need to worry about any of that, as Symfony has its own installer.
To follow along, you will need to install the installer :) Fortunately, it's copy / paste.
I'm going to assume you have followed the official guide and have the installer available on your local development machine. Note here that you only need the installer on your development machine - not on a production server / your real web server.
To get started then, I would head to a central directory on my computer where I store all my code projects. In my case, this is the /home/chris/Development
directory. The home
directory of the current user is often shortened to a ~
, so /home/chris/Development
and ~/Development
are one and the same.
Ok, so from my ~/Development
directory I would do symfony new contact-form
, e.g.:
➜ ~/Development symfony new contact-form
Downloading Symfony...
5.5 MiB/5.5 MiB ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 100%
Preparing project...
✔ Symfony 3.2.7 was successfully installed. Now you can:
* Change your current directory to /home/chris/Development/contact-form
* Configure your application in app/config/parameters.yml file.
* Run your application:
1. Execute the php bin/console server:start command.
2. Browse to the http://localhost:8000 URL.
* Read the documentation at http://symfony.com/doc
This downloads all the required code and gives you some useful info on getting your new Symfony application up and running.
Let's change into the new directory:
cd contact-form
And list the directory contents:
➜ contact-form ls -la
total 156
drwxr-xr-x 9 chris chris 4096 Apr 10 10:49 .
drwxrwxrwt 15 root root 28672 Apr 10 10:51 ..
drwxr-xr-x 4 chris chris 4096 Apr 10 10:49 app
drwxr-xr-x 2 chris chris 4096 Apr 10 10:49 bin
-rw-rw-rw- 1 chris chris 2292 Apr 10 10:49 composer.json
-rw-rw-rw- 1 chris chris 78082 Apr 10 10:49 composer.lock
-rw-r--r-- 1 chris chris 248 Apr 5 14:09 .gitignore
-rw-r--r-- 1 chris chris 978 Apr 5 14:09 phpunit.xml.dist
-rw-rw-rw- 1 chris chris 82 Apr 10 10:49 README.md
drwxr-xr-x 3 chris chris 4096 Apr 10 10:49 src
drwxr-xr-x 3 chris chris 4096 Apr 10 10:49 tests
drwxr-xr-x 5 chris chris 4096 Apr 10 10:49 var
drwxr-xr-x 15 chris chris 4096 Apr 10 10:49 vendor
drwxr-xr-x 3 chris chris 4096 Apr 5 14:12 web
There are lots of interesting directories and files in here that we will explore in more depth as we go through this series.
For now, as a quick run down we have:
app
- contains our project's configurationbin
- contains any Symfony specific binaries - such asconsole
- `composer.[json|lock] - used by Composer to determine what versions of third party code we use / have installed
.gitignore
- a useful helper file for git version control, which we do not have setup by defaultphpunit.xml.dist
- a useful helper file for PHPUnit test configREADME.md
- the ahem, readme file :/src
- where all our code will livetests
- one potential place our test code might livevar
- containscache
,logs
, andsessions
directories by defaultvendor
- the directory that composer installs all third party code intoweb
- the publicly visible folder out there on the web (contains images, css, etc)
Whilst in here, the first thing I would do is run a composer update
. Even though the Symfony installer will have installed the latest version of the Symfony Framework - 3.2.7
in our case - there may still be dependency updates available. Note that depending on your connection speed, this may take a few minutes:
➜ contact-form composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 0 installs, 4 updates, 0 removals
- Updating twig/twig (v1.33.0 => v2.3.0) Downloading: 100%
- Updating doctrine/collections (v1.3.0 => v1.4.0) Downloading: 100%
- Updating doctrine/annotations (v1.2.7 => v1.4.0) Downloading: 100%
- Updating doctrine/common (v2.6.2 => v2.7.2) Downloading: 100%
Writing lock file
Generating autoload files
> Incenteev\ParameterHandler\ScriptHandler::buildParameters
Updating the "app/config/parameters.yml" file
> Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::buildBootstrap
> Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache
// Clearing the cache for the dev environment with debug
// true
[OK] Cache for the "dev" environment (debug=true) was successfully cleared.
> Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::installAssets
Trying to install assets as relative symbolic links.
-- -------- ----------------
Bundle Method / Error
-- -------- ----------------
[OK] All assets were successfully installed.
> Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::installRequirementsFile
> Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::prepareDeploymentTarget
Now this will look a little nicer on your screen. Unfortunately in copy / pasting I do lose all the nice colourings, but hey ho.
Starting The Web Server
We don't need to have Apache, or nginx setup and working to start browsing our new Symfony web site. Starting from Symfony 2.6, we can start web server directly from the command line:
php bin/console server:start
This will start up a web server using PHP's built-in webserver. Be careful here, as it's easy to forget about it and leave it running:
php bin/console server:stop
If you are the forgetful type - like me - then you can use:
php bin/console server:run
This stops you from using the command line whilst the web server is running. You likely will need the command line for other things, so this option is less useful than running the web server in the background (server:start
/ server:stop
).
You can read more about the web server utility by clicking here.
Whichever method you choose, make sure to browse to the given link - http://127.0.0.1:8000
by default - and have a look around. We will be looking into the code that renders this page very shortly.
Working With Symfony In PHPStorm
I'm going to use PHPStorm for all my coding tasks throughout this series. It's the IDE I use in every video here on CodeReviewVideos, and the IDE I use in my day-to-day development also.
I have a bunch of videos on using PHPStorm in general, and with Symfony which I would recommend you check out if at all interested.
The only needed step is to start a new project, using the newly downloaded Symfony project as your project's directory. After that, I have no special setup steps - though you may wish to add the Symfony PHPStorm plugin as it comes with a bunch of useful extras to help you along (better auto completion being my favourite).
With my project setup, the first thing I am going to do is ensure we have Bootstrap styling available.
You may have already heard that Symfony has some presets available for styling with Bootstrap (along with Zurb Foundation). To use these styles we do need to have the Bootstrap (or Zurb Foundation) CSS in our project somewhere.
For simplicity, I'm going to pull in the Bootstrap CSS using the official Bootstrap CDN link. This just means I'm going to use the CSS via the web - and in a real project, you'd probably more likely want to have the CSS local to your webserver. This is an area of opinion, and for the moment, one I am going to completely side-step :)
Adding in Bootstrap is really easy. We simply need to grab the link
from [the Bootstrap documentation], and copy / paste into our Twig template:
<!DOCTYPE html>
<!-- /app/Resources/views/base.html.twig -->
<html>
<head>
<meta charset="UTF-8" />
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
We will cover this template in more detail shortly. For now, just paste in the Bootstrap provided link and save the file.
Whilst changing our templates around, I will also edit the other provided file - /app/Resources/views/default/index.html.twig
- to provide a starting point for our project:
<!-- /app/Resources/views/default/index.html.twig -->
{% extends 'base.html.twig' %}
{% block body %}
<div class="container">
your text goes here
</div>
{% endblock %}
{% block stylesheets %}
<style>
</style>
{% endblock stylesheets %}
Now, you should still be able to view your Symfony-powered website at http://127.0.0.1:8000
without having to restart the web server. Just refresh the page and behold, all the default Symfony stuff is gone, and instead, you should be able to see "your text goes here". Amazing! :)
Let's now look at how and why this works.
Default Controller - The 'C' in MVC
One of the terms you will hear frequently when working with a framework like Symfony, Laravel, Rails, or Django is MVC - short for Model View Controller.
This is a software design pattern name given to something you are very likely already doing, even if you don't call it as such.
From a very high level, most of our websites behave like this:
- A User "requests" a page on our site by browsing to one of our URLs
- Our code does something mundane, or extremely clever with this request data to figure out what that user wants, and...
- Returns a response - whether as HTML, or JSON, or XML, or whatever
Taking this over simplified version of things, we could then map:
- Model - "Our code does something mundane or extremely clever with this request data"
- View - a "HTML, or JSON, or XML, or whatever" representation of whatever data the "model" returns
- Controller - Receives the User's request, gets the Model to do the dirty work, and then passes the result onto the view - which the controller then returns to the end user
This is extremely simplified, and - in my opinion - a large part of the confusion around MVC is it's open to a very wide range of interpretations.
Anyway, the Symfony Framework currently comes with this DefaultController
class that is the reason why we can fire up the web server and immediately see something interesting.
Given that we have tweaked our templates as above, all we need at this point is the following code:
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class DefaultController extends Controller
{
/**
* @Route("/", name="homepage")
*/
public function indexAction()
{
return $this->render('default/index.html.twig');
}
}
Without a whole lot of understanding how this works behind the scenes, it's fairly easy to imagine how it might work:
We have a controller class: the DefaultController
.
Somehow Symfony is aware of this class, and will call functions inside it when routes configured inside it are requested.
So how is Symfony aware of this class? Well, Symfony comes pre-configured in this way thanks to an entry in routing.yml
:
# /app/config/routing.yml
app:
resource: "@AppBundle/Controller/"
type: annotation
routing.yml
is a file that Symfony will look at behind the scenes when loading all the routes in our application. We don't need to do anything to make this work.
What this entry is saying is that the routing loader should look inside the entire /src/AppBundle/Controller
directory, find any configured Controller classes, and then look for Route
annotations above any of the created controller actions.
We have one of those!
This DefaultController
has one public method - the indexAction
.
This indexAction
has a Route
annotation, which has the path of /
, and a name we're calling 'homepage':
/**
* @Route("/", name="homepage") <-- this is the routing annotation
*/
public function indexAction()
This path of /
means when we browse to http://127.0.0.1:8000/
(the trailing /
being the important thing here), we activate this controller action.
If our route pointed to the path of /beans
then browsing to http://127.0.0.1:8000/beans
would activate this route instead.
The more confusing part - in my opinion - is around the line:
return $this->render('default/index.html.twig');
So my first question might be: where does $this->render
come from? We haven't defined this method, so how can call it?
Well, if you're using PHPStorm - or any other decent IDE - you should be able to hold down the ctrl key and click the render
method, and be taken to the underlying implementation. This is a pretty good 'tip' to follow whenever you can't figure out how things work. It won't always work, unfortunately, but it will get you most of the way, most of the time.
Up to Symfony 3.2, this would be a protected method on the Controller
that the Symfony Framework Bundle provides, and that our DefaultController
extends.
If you look at the line:
class DefaultController extends Controller
You can see that our DefaultController extends Controller
.
This is inheritance.
Likewise, a few lines up we can see that this Controller
class that we are extend
ing from is actually something we are use
ing:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
By extending this controller we gain access to all of its public
and protected
methods, and indirectly its private
methods also, should it have any (which it doesn't).
As of Symfony 3.3 onwards this logic has been extracted out into the ControllerTrait
, which the base Controller
will use
. Truthfully this is geeky / nerdy stuff you don't need to know.
Both render
implementations are identical, so whichever version of Symfony you are using, this is fundamentally the same.
Now, how these templates get processed and rendered is a video's worth of content in itself. What happens here depends on your environment - dev
, or prod
, etc.
The important thing to note is that much like how routing.yml
is a default path for how routing works, the directory app/Resources/views
is also a special path, and will be used behind the scenes when we specify our template location as default/index.html.twig
.
I'm willing to go into further details on this template structure if interested - please do leave a comment below. Just be aware that whilst it's interesting, to actually use Symfony on a day to day basis, it is not critical to understand how this works in any way. There's plenty of other 'stuff' to know that is more urgent at this stage, so don't overwhelm yourself.
To finish up here we are going to rename the DefaultController
, and associated Twig template to SupportController.php
, and rename the index.html.twig
file from:
/app/Resources/views/default/index.html.twig
to
/app/Resources/views/support/index.html.twig
You do not need to do this, however it will make more logical sense to keep things named in line with what function they are expected to do.
We've already started to do a lot of the common things you will do in almost every Symfony application you will ever work with. A large part of feeling confident and comfortable with Symfony is simply using it frequently.
Let's continue on now and make our contact form display in our web browser.