Created
October 27, 2014 17:33
-
-
Save KaduNovoK/9944e5195bcd758897e0 to your computer and use it in GitHub Desktop.
symfony 2 - best practices
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
01 - Always use Composer to install Symfony | |
test: $ composer --version | |
install: $ curl -sS https:://getcomposer.org/installer | php | |
$ sudo mv composer.phar /usr/local/bin/composer | |
02 - Creating a application | |
$ cd projects/ | |
$ composer create-project symfony/framework-standard-edition blog/ | |
$ cd blog/ | |
$ php app/console --version | |
$ php app/check.php | |
03 - Create ony one Bundle called AppBundle for your application logic | |
$ php app/console generate:bundle --namespace=AppBundle --dir=src --format=annotation --no-interation | |
04 - Define the infrastructure-related configuration options in the app/config/parameters.yml file | |
05 - Define all your application's parameters in the app/config/parameters.yml.dist | |
06 - Define the application behavior related configuration options in the app/config/config.yml file | |
07 - Use constants to define configuration options that rarely change | |
08 - Don't define a semantic dependency injection configuration for your bundles | |
09 - The name of your application's services should be as short as possible, ideally just one simple word | |
php: | |
namespace AppBundle\Utils; | |
class Slugger | |
{ | |
public function slugify($string) | |
{ | |
return pre_replace( | |
'/[^a-z0-9]/', '-', strtolower(trim(strip_tags($string))) | |
); | |
} | |
} | |
yml: | |
# app/config/services.yml | |
services: | |
slugger: # instead app.utils.slugger | |
class: AppBundle\Utils\Slugger | |
into controller: | |
// ... | |
$slug = $this->get('slugger')->slugify($string); | |
10 - Use the YAML format to define your own services | |
11 - Don't define parameters for the classes of your services | |
12 - Use notations to define the mapping information of the Doctrine entities | |
13 - Use fixtures | |
$ composer require "doctrine/doctrine-fixtures-bundle" | |
enable the bundle: | |
if (in_array($this->getEnvironment(), array('dev', 'test'))) { | |
// ... | |
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(), | |
} | |
loading fixtures: | |
$ php app/console doctrine:fixtures:load | |
14 - Use the 5-10-20 rule, your controllers should define 5 variables or less, contain 10 actions or less and include 20 lines or less in each action. | |
15 - Make your controller extend the FrameworkBundle base controller and use annotations to configure routing, caching and security whenever possible. | |
to load route anotations of controlers: | |
# app/config/routing.yml | |
app: | |
resource: "@AppBundle/Controller" | |
type: annotation | |
16 - Don't use the @Template() annotation to configure the template used by the controller | |
use: $this->render('path/file.html.twig', array('foo' => 'bar')); | |
17 - Use the ParamConverter trick to automatically query for Doctrine entities when it's simple and convenient | |
sample: | |
/** | |
* @Route("/comment/{postSlug}/new", name = "comment_new") | |
*/ | |
public function newAction(Request $request, $postSlug) | |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | |
/** | |
* @Route("/comment/{postSlug}/new", name = "comment_new") | |
* @ParamConverter("post", options={"mapping": {"postSlug": "slug"}}) | |
*/ | |
public function newAction(Request $request, Post $post) | |
18 - Use Twig | |
19 - Store all your application's templates in app/Resources/views/ directory | |
20 - Define your twig extensions in the AppBundle/Twig/ directory and configure them using the app/config/services.yml file | |
$ composer require erusev/parsedown | |
# app/config/services.yml | |
services: | |
# ... | |
markdown: | |
class: AppBundle\Utils\Markdown | |
namespace AppBundle\Utils; | |
class Markdown | |
{ | |
private $parser; | |
public function __construct() | |
{ | |
$this->parser = new \Parsedown(); | |
} | |
public function toHtml($text) | |
{ | |
$html = $this->parser->text($text); | |
return $html; | |
} | |
} | |
namespace AppBundle\Twig; | |
use AppBundle\Utils\Markdown; | |
class AppExtension extends \Twig_Extension | |
{ | |
private $parser; | |
public function __construct(Markdown $parser) | |
{ | |
$this->parser = $parser; | |
} | |
public function getFilters() | |
{ | |
return array( | |
new \Twig_SimpleFilter( | |
'md2html', | |
array($this, 'markdownToHtml'), | |
array('is_safe' => array('html')) | |
), | |
); | |
} | |
public function markdownToHtml($content) | |
{ | |
return $this->parser->toHtml($content); | |
} | |
public function getName() | |
{ | |
return 'app_extension'; | |
} | |
} | |
# app/config/services.yml | |
services: | |
app.twig.app_extension: | |
class: AppBundle\Twig\AppExtension | |
arguments: ["@markdown"] | |
tags: | |
- { name: twig.extension } | |
21 - Add buttons in the templates, not in the form classes or the controllers | |
22 - Don't use the form() or form_start() functions to render the starting and ending | |
form tags (the delete is exception) | |
23 - Use the XLIFF format for your translation files | |
24 - Store the translation files in the app/Resources/translations/ directory | |
25 - Always use keys for translations instead of content strings | |
26 - Unless you have two legitimately different authentication systems and users (e.g. form | |
login for the main site and a token system for your API only), we recommend having | |
only one firewall entry with the anonymous key enabled | |
27 - Use the bcrypt encoder for encoding your users' passwords | |
security: | |
encoders: | |
AppBundle\Entity\User: bcrypt | |
providers: | |
database_users: | |
entity: { class: AppBundle:User, property: username } | |
firewalls: | |
secured_area: | |
pattern: ^/ | |
anonymous: true | |
form_login: | |
check_path: security_login_check | |
login_path: security_login_form | |
logout: | |
path: security_logout | |
target: homepage | |
28 - For protecting broad URL patterns, use access_control | |
29 - Whenever possible, use the @Security annotation | |
30 - Check security directly on the security.context service whenever you | |
have a more complex situation | |
31 - For fine-grained restrictions, define a custom security voter | |
32 - For restricting access to any object by any user via an admin interface, use | |
the Symfony ACL | |
33 - Use the security annotation: | |
/** | |
* Displays a form to create a new Post entity. | |
* | |
* @Route("/new", name="admin_post_new") | |
* @Security("has_role('ROLE_ADMIN')") | |
*/ | |
public function newAction() | |
use AppBundle\Entity\Post; | |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; | |
/** | |
* @Route("/{id}/edit", name="admin_post_edit") | |
* @Security("user.getEmail() == post.getAuthorEmail()") | |
*/ | |
public function editAction(Post $post) | |
34 - Use Assetic to compile, combine and minimize web assets, unless you're comfortable | |
with frontend tools like GruntJS | |
35 - Define a functional test that at least checks if your application pages are successfully | |
loading | |
/** @dataProvider provideUrls */ | |
public function testPageIsSuccessful($url) | |
{ | |
$client = self::createClient(); | |
$client->request('GET', $url); | |
$this->assertTrue($client->getResponse()->isSuccessful()); | |
} | |
public function provideUrls() | |
{ | |
return array( | |
array('/'), | |
array('/posts'), | |
array('/post/fixture-post-1'), | |
array('/blog/category/fixture-category'), | |
array('/archives'), | |
// ... | |
); | |
} | |
36 - Hardcode the URLs used in the functional tests instead of using the URL generator |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment