2. Who we are?
• locastic – since 2010
• Web and mobile development
• UI/UX design
• Located in Split, Croatia
• 9 team members
• www.locastic.com
• studio@locastic.com
4. Speakers?
• Antonio Perić-Mažar,
mag. ing. comp.
• CEO and partner @ locastic
• Last 6 years developing
custom web apps
• Last 3 years developing
custom apps with Symfony
• www.locastic.com
• antonio@locastic.com
• twitter: @antonioperic
5. Speakers?
• Luka Vidoš, mag. Diz.
• Frontend Dev @ locastic
●
Last 13 years designing, slicing
and implementing beautiful
web design into various web
apps
●
In love with Symfony for
last 3 years
• www.locastic.com
• luka@locastic.com
• twitter: @lukavidos
6. Symfony is project
●
PHP framework
●
Philosophy
●
Community
• Fabien Potencier,
SensioLabs (France)
• 2005. first version was relased
• 2007. symfony 1.0
• 2011. Symfony2
• ATM Symfony 2.3.4 (LTS)
7. 10 criteria for choosing the correct
framework
1. Popularity and community size
2. Philosophy
3. Sustainability
4. Support
5. Technique
6. Security
7. Documentation ( always can be better )
8. License (free)
9. Availability of resources on the market
10. Try it out!
8. 6 good reasons to use Symfony
1. Reputation
2. Permanence
3. References (Yahoo!, Dailymotion, Opensky.com, Exercise.com, phpBB, Drupal,
eZPublish,Youporn :) )
4. Innovation
5. Resources
6. Interoperability
9. The technological benefits of
Symfony in 6 easy lessons
1. Faster and less greedy
2. Unlimited flexibility
3. Expandable
4. Stable and sustainable
5. The joy of developing
6. Ease of use
14. To do list app
●
Creating unlimited number of task lists
●
Creating, deleting and updating lists
●
Creating, deleting and updating tasks inside of
lists
21. Standalone Tools: The Symfony2
Components
HttpFoundation - Contains the Request and Response classes, as well as other classes for handling
sessions and file uploads;
Routing - Powerful and fast routing system that allows you to map a specific URI (e.g. /contact) to some
information about how that request should be handled (e.g. execute the contactAction() method);
Form - A full-featured and flexible framework for creating forms and handling form submissions;
Validator A system for creating rules about data and then validating whether or not user-submitted
data follows those rules;
ClassLoader An autoloading library that allows PHP classes to be used without needing to manually
require the files containing those classes;
Templating A toolkit for rendering templates, handling template inheritance (i.e. a template is
decorated with a layout) and performing other common template tasks;
Security - A powerful library for handling all types of security inside an application;
Translation A framework for translating strings in your application.
22. Coding standard
Structure
●
Add a single space after each comma delimiter;
●
Add a single space around operators (==, &&, ...);
●
Add a comma after each array item in a multi-line array, even after the last one;
●
Add a blank line before return statements, unless the return is alone inside a statement-group (like an if
statement);
●
Use braces to indicate control structure body regardless of the number of statements it contains;
●
Define one class per file - this does not apply to private helper classes that are not intended to be instantiated
from the outside and thus are not concerned by the PSR-0 standard;
●
Declare class properties before methods;
●
Declare public methods first, then protected ones and finally private ones;
●
Use parentheses when instantiating classes regardless of the number of arguments the constructor has;
●
Exception message strings should be concatenated using sprintf.
23. Coding standard
Naming Conventions
●
Use camelCase, not underscores, for variable, function and method names, arguments;
●
Use underscores for option names and parameter names;
●
Use namespaces for all classes;
●
Prefix abstract classes with Abstract. Please note some early Symfony2 classes do not follow this convention and
have not been renamed for backward compatibility reasons. However all new abstract classes must follow this
naming convention;
●
Suffix interfaces with Interface;
●
Suffix traits with Trait;
●
Suffix exceptions with Exception;
●
Use alphanumeric characters and underscores for file names;
●
Don't forget to look at the more verbose Conventions document for more subjective naming considerations.
24. Coding standard
Service Naming Conventions
●
A service name contains groups, separated by dots;
●
The DI alias of the bundle is the first group (e.g. fos_user);
●
Use lowercase letters for service and parameter names;
●
A group name uses the underscore notation;
●
Each service has a corresponding parameter containing the class name, following the SERVICE
NAME.class convention.
Documentation
●
Add PHPDoc blocks for all classes, methods, and functions;
●
Omit the @return tag if the method does not return anything;
●
The @package and @subpackage annotations are not used.
26. Coding standard
/**
* @param string $dummy Some argument description
*/
public function __construct($dummy)
{
$this->fooBar = $this->transformText($dummy);
}
27. Coding standard
/**
* @param string $dummy Some argument
description
* @param array $options
*
* @return string|null Transformed input
*/
private function transformText($dummy,
array $options = array())
28. Coding standard
if (true === $dummy) {
return;
}
if ('string' === $dummy) {
if ('values' === $mergedOptions['some_default']) {
return substr($dummy, 0, 5);
}
return ucwords($dummy);
}
throw new RuntimeException(sprintf('Unrecognized dummy option "%s"', $dummy));
}
}
29. Creating Pages in Symfony2
Creating a new page in Symfony2 is a simple two-
step process:
●
Create a route: A route defines the URL (e.g. /about) to your page and
specifies a controller (which is a PHP function) that Symfony2 should
execute when the URL of an incoming request matches the route path;
●
Create a controller: A controller is a PHP function that takes the
incoming request and transforms it into the Symfony2 Response object
that's returned to the user.
●
Environment:
●
Prod, dev, test
30. But before... bundles :)
●
Bundle is everything in Symfony2 :) - first-
class citizens
●
Directory that houses everything related to a
specific feature ( configuration, PHP, JS, CSS...)
●
We can compare it with modul or plugin
●
Flexible, independent, powerful
> php app/console generate:bundle
32. Creating Pages in Symfony2
Step2: Create controller
// src/Acme/HelloBundle/Controller/HelloController.php
namespace AcmeHelloBundleController;
use SymfonyComponentHttpFoundationResponse;
class HelloController
{
public function indexAction($name)
{
return new Response('<html><body>Hello '.$name.'!</body></html>');
}
}
33. Creating Pages in Symfony2
Step3: Create template
// src/Acme/HelloBundle/Controller/HelloController.php
namespace AcmeHelloBundleController;
use SymfonyBundleFrameworkBundleControllerController;
class HelloController extends Controller
{
public function indexAction($name)
{
return $this->render(
'AcmeHelloBundle:Hello:index.html.twig',
array('name' => $name)
);
}
}
34. Creating Pages in Symfony2
Practice #1:
- create controller that receives some integer as
parameter from route and returns template
with that number squared.
Time: 5 minutes
35. Controller
• Request -> Response
●
The response could be an HTML page, an XML document,
a serialized JSON array, an image, a redirect, a 404 error
or anything else you can dream up.
●
The controller contains whatever arbitrary logic your
application needs to render the content of a page.
use SymfonyComponentHttpFoundationResponse;
public function helloAction()
{
return new Response('Hello world!');
}
38. Mapping url to a Controller
# app/config/routing.yml
hello:
path: /hello/{first_name}/{last_name}
defaults: { _controller: AcmeHelloBundle:Hello:index, color: green }
// order of arguments does not metter
public function indexAction($first_name, $color, $last_name)
{
// ... do whatever logic and return Response
}
39. The Request as Controller
Argument
use SymfonyComponentHttpFoundationRequest;
public function updateAction(Request $request)
{
$form = $this->createForm(...);
$form->handleRequest($request);
// ...
}
40. The Base Controller Class
// src/Acme/HelloBundle/Controller/HelloController.php
namespace AcmeHelloBundleController;
use SymfonyBundleFrameworkBundleControllerController;
use SymfonyComponentHttpFoundationResponse;
class HelloController extends Controller
{
// ... do whatever logic and return Response
}
By extending this Controller class, you can take advantage of several helper
methods.
41. Common Controller Tasks
// redirecting
public function indexAction()
{
return $this->redirect($this->generateUrl('homepage'));
}
public function indexAction()
{
return $this->redirect($this->generateUrl('homepage'), 301);
}
Shortcut for:
use SymfonyComponentHttpFoundationRedirectResponse;
return new RedirectResponse($this->generateUrl('homepage'));
42. Common Controller Tasks
// forwarding
public function indexAction($name)
{
$response = $this->forward('AcmeHelloBundle:Hello:fancy', array(
'name' => $name,
'color' => 'green',
));
// ... further modify the response or return it directly
return $response;
}
public function fancyAction($name, $color)
{
// ... create and return a Response object
}
46. 404 error
public function indexAction()
{
// retrieve the object from database
$product = ...;
if (!$product) {
throw $this->createNotFoundException('The product does not exist');
}
return $this->render(...);
}
Error 500
throw new Exception('Something went wrong!');
49. Managing the Session
$session = $this->getRequest()->getSession();
// store an attribute for reuse during a later user request
$session->set('foo', 'bar');
// in another controller for another request
$foo = $session->get('foo');
// use a default value if the key doesn't exist
$filters = $session->get('filters', array());
50. Flash Messages
public function updateAction()
{
$form = $this->createForm(...);
$form->bind($this->getRequest());
if ($form->isValid()) {
// do some sort of processing
$this->get('session')->getFlashBag()->add('notice', 'Your changes were saved!');
return $this->redirect($this->generateUrl(...));
}
return $this->render(...);
}
51. Flash Messages
{% for flashMessage in app.session.flashbag.get('notice') %}
<div class="flash-notice">
{{ flashMessage }}
</div>
{% endfor %}
52. The Response Object
The only requirement for a controller is to return a Response object. The Response
class is a PHP abstraction around the HTTP response - the text-based message filled
with HTTP headers and content that's sent back to the client:
use SymfonyComponentHttpFoundationResponse;
// create a simple Response with a 200 status code (the default)
$response = new Response('Hello '.$name, 200);
// create a JSON-response with a 200 status code
$response = new Response(json_encode(array('name' => $name)));
$response->headers->set('Content-Type', 'application/json');
53. The Response Object - JSON
use SymfonyComponentHttpFoundationResponse;
$response = new Response();
$response->setContent(json_encode(array(
'data' => 123,
)));
$response->headers->set('Content-Type', 'application/json');
---
use SymfonyComponentHttpFoundationJsonResponse;
$response = new JsonResponse();
$response->setData(array(
'data' => 123
));
54. The Response Object - FILE
use SymfonyComponentHttpFoundationResponseHeaderBag;
$d = $response->headers-
>makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'foo.pdf');
$response->headers->set('Content-Disposition', $d);
---
use SymfonyComponentHttpFoundationBinaryFileResponse
$file = 'path/to/file.txt';
$response = new BinaryFileResponse($file);
55. The Request Object
$request = $this->getRequest();
$request->isXmlHttpRequest(); // is it an Ajax request?
$request->getPreferredLanguage(array('en', 'fr'));
$request->query->get('page'); // get a $_GET parameter
$request->request->get('page'); // get a $_POST parameter
56. Render static page
No need for controller!!!
acme_privacy:
path: /privacy
defaults:
_controller: FrameworkBundle:Template:template
template: 'AcmeBundle:Static:privacy.html.twig'
57. Controller
Practice #2:
●
send numbers via $_GET parameter (numA and numB). Get these two
numbers in controller from Request Object (using $_GET[] is forbidden).
Send Request object as action's parameter.
If numA*numB > 100 redirect to some custom controller's action;
If numA < 0 and numB < 0 throw 404 error;
If numA+numB > 10 return JSON Response with result;
else return template with result numA-numB;
Time: 10 minutes
58. Routing <3
index.php?article_id=57 -> don't like this
/i-like/symfony-routing
One of the most powerfull Symfony2 component. Very flexible!!!
Route = map form url to controller
Position of route is important!!!
72. Earlier Routes always Win!!!
What this all means is that the order of the
routes is very important. If the blog_show
route were placed above the blog route, the
URL /blog/2 would match blog_show instead
of blog since the {slug} parameter of
blog_show has no requirements. By using
proper ordering and clever requirements, you
can accomplish just about anything.
73. Adding Requirements
homepage:
path: /{culture}
defaults: { _controller: AcmeDemoBundle:Main:homepage, culture: en }
requirements:
culture: en|fr
/ {culture} = en
/en {culture} = en
/fr {culture} = fr
/es won't match this route
78. Visualizing & Debugging Routes
While adding and customizing routes, it's helpful to be able to visualize and get
detailed information about your routes. A great way to see every route in your
application is via the router:debug console command. Execute the command by
running the following from the root of your project.
> php app/console router:debug
79. Generating URLs
class MainController extends Controller
{
public function showAction($slug)
{
// ...
$url = $this->generateUrl( // helper method
'blog_show',
array('slug' => 'my-blog-post')
);
}
}
81. Generating URLs
// with query string
$this->get('router')->generate('blog', array('page' => 2, 'category' => 'Symfony'));
// /blog/2?category=Symfony
// from template
<a href="{{ path('blog_show', {'slug': 'my-blog-post'}) }}">
Read this blog post.
</a>
<a href="{{ url('blog_show', {'slug': 'my-blog-post'}) }}">
Read this blog post.
</a>
82. Routing :)
Practice #3:
- create route for AcmeDemoBundle:Hello:list, that accepts parameters:
- category (string with no numbers)
- page number (integer)
- only GET method
In template list: category, slug and page number like this:
If url is: /ez-publish-summer-camp/3
Category slug: ez-publish-summer-camp
Page number: 3
Use YAML format for routing!
Time: 5 minutes
88. MODEL
A bundle can accept only one metadata definition format. For
example, it's not possible to mix YAML metadata definitions with
annotated PHP entity class definitions.
Be careful that your class name and properties aren't mapped to a
protected SQL keyword (such as group or user).
Create getters and setters
> php app/console doctrine:generate:entities
Acme/StoreBundle/Entity/Product
> php app/console doctrine:schema:update –force
89. Persist object to database
// src/Acme/StoreBundle/Controller/DefaultController.php
// ...
use AcmeStoreBundleEntityProduct;
use SymfonyComponentHttpFoundationResponse;
public function createAction()
{
$product = new Product();
$product->setName('A Foo Bar');
$product->setPrice('19.99');
$product->setDescription('Lorem ipsum dolor');
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
return new Response('Created product id '.$product->getId());
}
90. Fetching Objects from the
Database
public function showAction($id)
{
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Product')
->find($id);
if (!$product) {
throw $this->createNotFoundException(
'No product found for id '.$id
);
}
// ... do something, like pass the $product object into a template
}
91. Repository Basic
// query by the primary key (usually "id")
$product = $repository->find($id);
// dynamic method names to find based on a column value
$product = $repository->findOneById($id);
$product = $repository->findOneByName('foo');
// find *all* products
$products = $repository->findAll();
// find a group of products based on an arbitrary column value
$products = $repository->findByPrice(19.99);
92. Repository Basic
// query for one product matching be name and price
$product = $repository->findOneBy(array('name' => 'foo', 'price' => 19.99));
// query for all products matching the name, ordered by price
$products = $repository->findBy(
array('name' => 'foo'),
array('price' => 'ASC')
);
94. Updating an Object
public function updateAction($id)
{
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AcmeStoreBundle:Product')->find($id);
if (!$product) {
throw $this->createNotFoundException(
'No product found for id '.$id
);
}
$product->setName('New product name!');
$em->flush();
return $this->redirect($this->generateUrl('homepage'));
}
96. Querying for Object with DQL
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT p
FROM AcmeStoreBundle:Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', '19.99');
$products = $query->getResult();
101. Custom Repository Classes
// src/Acme/StoreBundle/Entity/ProductRepository.php
namespace AcmeStoreBundleEntity;
use DoctrineORMEntityRepository;
class ProductRepository extends EntityRepository
{
public function findAllOrderedByName()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC'
)
->getResult();
}
}
111. Saving related entities
// ...
use AcmeStoreBundleEntityCategory;
use AcmeStoreBundleEntityProduct;
use SymfonyComponentHttpFoundationResponse;
class DefaultController extends Controller
{
public function createProductAction()
{
$category = new Category();
$category->setName('Main Products');
$product = new Product();
$product->setName('Foo');
$product->setPrice(19.99);
// relate this product to the category
$product->setCategory($category);
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->persist($product);
112. Fetching related Objects
public function showAction($id)
{
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Product')
->find($id);
$categoryName = $product->getCategory()->getName();
// ...
}
114. Fetching related Objects
public function showProductAction($id)
{
$category = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Category')
->find($id);
$products = $category->getProducts();
// ...
}
115. Doctrine Field Types Reference
Strings
string (used for shorter strings)
text (used for larger strings)
Numbers
integer
smallint
bigint
decimal
float
Dates and Times (use a DateTime object for these fields in PHP)
date
time
datetime
Other Types
boolean
object (serialized and stored in a CLOB field)
array (serialized and stored in a CLOB field)
116. Some advanced stuff
Events and lifecycle callbacks
preRemove
postRemove
prePersist
postPersist
preUpdate
postUpdate
postLoad
LoadClassMetadata
Doctrine Extensions: Timestampable, Sluggable, etc.
117. MODEL
Practice #3:
- Create entities for schema bellow and create empty custom repository for
each, use XML
Time: 15 minutes
121. CRUD
→ create bundle
→ create entity
→ create full CRUD* with routes, controllers and templates
122. CRUD
→ create bundle
→ create entity
→ create full CRUD with routes, controllers and templates
→ use console :)
123. FORMS AND VALIDATION
Workshop: Symfony2 Forms – tomorrow 09:00am
Bernhard Schussek
The Symfony2 Form component helps you to build powerful forms with little code. This workshop shows you
how to use the component in your daily life.
Basically, a framework consists of: A toolbox - a set of prefabricated, rapidly integratable software components. This means that you will have to write less code, with less risk of error. This also means greater productivity and the ability to devote more time to doing those things which provide greater added value, such as managing guiding principles, side effects, etc. Best Practices guaranteed! A methodology – an “assembly diagram” for applications. A structured approach may seem constraining at first. But in reality it allows developers to work both efficiently and effectively on the most complex aspects of a task, and the use of Best Practices guarantees the stability, maintainability and upgradeability of the applications you develop.