SlideShare a Scribd company logo
@jessicamauerhan | Madison PHP | https://joind.in/16022
Rebuilding Our Foundation
How We Used Symfony To
Rewrite Our Application
@jessicamauerhan | Madison PHP | https://joind.in/16022
@jessicamauerhan | Madison PHP | https://joind.in/16022
● Project History
● Problems and Goals
● Why Symfony?
● Rapid Development
of Quality Code
● Doctrine DBAL / ORM
Topics
● Automated Testing
● Sonata Admin Bundle
● Creating an API with
Symfony
● Dependency Injection
● Current Project
Status
2
@jessicamauerhan | Madison PHP | https://joind.in/16022
● Learning Management System
● Content Production
● E-Commerce
● Business to Business (B2B)
Application Summary
3
@jessicamauerhan | Madison PHP | https://joind.in/16022
Problematic History
● Broken Admin Panel
● No Documentation of Basic Processes
● Frontend Site Worked, Progress Stalled
● Complex Logic not Documented
4
@jessicamauerhan | Madison PHP | https://joind.in/16022
Business Goals
● Add Missing Admin Panel Functionality
● Add New Features Without Breaking Existing Features
● Avoid Downtime
5
@jessicamauerhan | Madison PHP | https://joind.in/16022
Technical Goals
● Maintainable Code
● Quality Code
● Documentation
● Rapid Development
● Easy Deployment
● Zero Regressions Per Release
6
@jessicamauerhan | Madison PHP | https://joind.in/16022
Why Symfony?
● Community
○ Third Party Code Integration
○ Blazing Trails
○ Popularity
○ Support
7
@jessicamauerhan | Madison PHP | https://joind.in/16022
Why Symfony?
● Technology
○ Dependency Injection & Decoupling
○ Unit Testing
○ Functional Testing
○ Behavior Testing
8
@jessicamauerhan | Madison PHP | https://joind.in/16022
Rapid Development of Quality Code
● Version Control: Git
● Development Workflow
9
@jessicamauerhan | Madison PHP | https://joind.in/16022
Image sourced from Atlassian: https://www.atlassian.com/git/tutorials/comparing-
workflows/gitflow-workflow
Licensed under the Creative Commons Attribution 2.5 Australia License.
10
@jessicamauerhan | Madison PHP | https://joind.in/16022
Rapid Development of Quality Code
● Version Control: Git
● Development Workflow: Gitflow
● Code Style Guide: PSR-2
11
@jessicamauerhan | Madison PHP | https://joind.in/16022
Rapid Development of Quality Code
● Version Control: Git
● Development Workflow: Gitflow
● Code Style Guide: PSR-2
● Code Quality Rules
12
@jessicamauerhan | Madison PHP | https://joind.in/16022
PHP Mess Detector
$ php composer require "phpmd/phpmd" --dev
$ php composer install --no-dev
13
@jessicamauerhan | Madison PHP | https://joind.in/16022
PHP Mess Detector Rules
<?xml version="1.0"?>
<ruleset name="Code Quality"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="
http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>Custom Code Quality Rules</description>
<!--Rulesets: http://phpmd.org/rules/index.html-->
<rule ref="rulesets/cleancode.xml"/>
<rule ref="rulesets/naming.xml/ShortVariable">
<properties>
<property name="minimum" value="4"/>
</properties>
</rule>
</ruleset>
14
@jessicamauerhan | Madison PHP | https://joind.in/16022
Rapid Development of Quality Code
● Version Control
● Development Workflow: Gitflow
● Code Style Guide: PSR-2
● Code Quality Rules: PHP Mess Detector
● Code Quality Enforcement
15
@jessicamauerhan | Madison PHP | https://joind.in/16022
Git Pre-Commit Code Quality Hook
#!/usr/bin/env php
<?php
require __DIR__ . '/../../vendor/autoload.php';
use SymfonyComponentConsoleApplication;
class CodeQualityTool extends Application
{
private $projectRoot;
const PHP_FILES_IN_SRC = '/^src/(.*)(.php)$/';
const PHP_FILES_IN_APPLICATION = '/^application/(.*)(.php)$/';
public function __construct()
{
$this->projectRoot = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..'
. DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR);
parent::__construct('Code Quality Tool', '1.0.0');
}
}
16
@jessicamauerhan | Madison PHP | https://joind.in/16022
Git Pre-Commit Code Quality Hook
private function extractCommitedFiles()
{
$files = [];
$output = [];
exec("git diff --cached --name-status --diff-filter=ACM", $output);
foreach ($output as $line) {
$filename = trim(substr($line, 1));
$isAppFile = preg_match(self::PHP_FILES_IN_APPLICATION, $filename);
$isSrcFile = preg_match(self::PHP_FILES_IN_SRC, $filename);
if ($isAppFile || $isSrcFile) {
$files[] = $filename;
}
}
return $files;
}
17
@jessicamauerhan | Madison PHP | https://joind.in/16022
Git Pre-Commit Code Quality Hook
private function checkPhpMd($files)
{
$succeed = true;
foreach ($files as $file) {
$processArgs = ['bin/phpmd', $file, 'text', 'phpmd-rules.xml'];
$processBuilder = new ProcessBuilder($processArgs);
$processBuilder->setWorkingDirectory($this->projectRoot);
$process = $processBuilder->getProcess();
$process->run();
if (!$process->isSuccessful()) {
$this->output->writeln($file);
$this->output->writeln($process->getErrorOutput());
$this->output->writeln($process->getOutput());
$succeed = false;
}
}
return $succeed;
}
18
@jessicamauerhan | Madison PHP | https://joind.in/16022
Git Pre-Commit Code Quality Hook
public function doRun(InputInterface $input, OutputInterface $output) {
$this->input = $input;
$this->output = $output;
$output->writeln('<info>Fetching files</info>');
$files = $this->extractCommitedFiles();
$info = '<info>Checking for messy code with PHPMD</info>';
$output->writeln($info);
if (!$this->checkPhpMd($files)) {
throw new Exception(sprintf('There are PHPMD violations!'));
}
}
19
@jessicamauerhan | Madison PHP | https://joind.in/16022
Rapid Development of Quality Code
● Version Control: Git
● Development Workflow: Gitflow
● Code Style Guide: PSR-2
● Code Quality Rules: PHP Mess Detector
● Code Quality Enforcement: Git Hooks
20
@jessicamauerhan | Madison PHP | https://joind.in/16022
Doctrine
21
@jessicamauerhan | Madison PHP | https://joind.in/16022
Doctrine DBAL / ORM
● Database Abstraction Layer (DBAL)
○ Database Vendor Agnostic
○ Query Builder
● Object Relational Mapper (ORM)
○ Maps Objects to Tables, Properties to Fields
○ Object Relationships
○ DQL - it's like SQL for your Objects
22
@jessicamauerhan | Madison PHP | https://joind.in/16022
Generating Entity Classes (Annotations)
$ php app/console doctrine:mapping:import --force AcmeBlogBundle xml
$ php app/console doctrine:mapping:convert annotation ./src
$ php app/console doctrine:generate:entities AcmeBlogBundle
23
@jessicamauerhan | Madison PHP | https://joind.in/16022
Annotated Entity Class
<?php
namespace AcmeBlogBundleEntity;
use DoctrineORMMapping as ORM;
/**
* AcmeBlogBundleEntityBlogComment
*
* @ORMTable(name="blog_comment")
* @ORMEntity
*/
class BlogComment
{
/**
* @var integer $id
* @ORMColumn(name="id", type="bigint")
* @ORMId
* @ORMGeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string $author
* @ORMColumn(name="author", type="string", length=100, nullable=false)
*/
private $author;
}
24
@jessicamauerhan | Madison PHP | https://joind.in/16022
Other Doctrine Tools
● Doctrine Migrations
● Data Fixtures Library
25
@jessicamauerhan | Madison PHP | https://joind.in/16022
Introducing Automated Tests
26
@jessicamauerhan | Madison PHP | https://joind.in/16022
$ php composer require "behat/behat" --dev
Installing Behat
27
@jessicamauerhan | Madison PHP | https://joind.in/16022
Writing Features for Existing Code
● Write Feature
● Run Test
○ Test Passes - Double Check
○ Test Fails
■ Described Feature Wrong
■ Mistake in Test Code
■ Feature is Broken
28
@jessicamauerhan | Madison PHP | https://joind.in/16022
New Symfony Development
29
@jessicamauerhan | Madison PHP | https://joind.in/16022
Installing Symfony Framework
30
@jessicamauerhan | Madison PHP | https://joind.in/16022
Installing Symfony Framework
31
@jessicamauerhan | Madison PHP | https://joind.in/16022
Merging Symfony Framework with Existing Code
● Install Separately
● Move Directories
● Combine composer.json
○ scripts
○ extra
○ autoload
32
@jessicamauerhan | Madison PHP | https://joind.in/16022
Generate App Bundle
33
@jessicamauerhan | Madison PHP | https://joind.in/16022
Generate App Bundle
C:wampwwwtest>php appconsole generate:bundle
Welcome to the Symfony2 bundle generator
Your application code must be written in bundles. This command helps
you generate them easily.
Each bundle is hosted under a namespace (like Acme/Bundle/BlogBundle).
The namespace should begin with a "vendor" name like your company name, your
project name, or your client name, followed by one or more optional category
sub-namespaces, and it should end with the bundle name itself
(which must have Bundle as a suffix).
See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#index-1
for more details on bundle naming conventions.
Use / instead of  for the namespace delimiter to avoid any problem.
Bundle namespace: Demo/AppBundle
34
@jessicamauerhan | Madison PHP | https://joind.in/16022
Generate App Bundle
In your code, a bundle is often referenced by its name. It can be the
concatenation of all namespace parts but it's really up to you to come
up with a unique name (a good practice is to start with the vendor name).
Based on the namespace, we suggest DemoAppBundle.
Bundle name [DemoAppBundle]: AppBundle
35
@jessicamauerhan | Madison PHP | https://joind.in/16022
The bundle can be generated anywhere. The suggested default directory uses the
standard conventions.
Target directory [C:wampwwwtest/src]:
Determine the format to use for the generated configuration.
Configuration format (yml, xml, php, or annotation): annotation
To help you get started faster, the command can generate some code snippets for
you.
Do you want to generate the whole directory structure [no]? yes
Generate App Bundle
36
@jessicamauerhan | Madison PHP | https://joind.in/16022
Generate App Bundle
Summary before generation
You are going to generate a "DemoAppBundleAppBundle" bundle
in "C:wampwwwtest/src/" using the "annotation" format.
Do you confirm generation [yes]?
Bundle generation
Generating the bundle code: OK
Checking that the bundle is autoloaded: OK
Confirm automatic update of your Kernel [yes]?
Enabling the bundle inside the Kernel: OK
Confirm automatic update of the Routing [yes]?
Importing the bundle routing resource: OK
You can now start using the generated code!
37
@jessicamauerhan | Madison PHP | https://joind.in/16022
Sonata Admin
38
@jessicamauerhan | Madison PHP | https://joind.in/16022 39
@jessicamauerhan | Madison PHP | https://joind.in/16022 40
@jessicamauerhan | Madison PHP | https://joind.in/16022
$ php composer require "sonata-project/admin-bundle"
$ php composer require "sonata-project/doctrine-orm-admin-bundle"
Install Sonata Admin
41
@jessicamauerhan | Madison PHP | https://joind.in/16022
//AppKernel.php
public function registerBundles()
{
return [
//...
new SymfonyBundleSecurityBundleSecurityBundle(),
new SonataCoreBundleSonataCoreBundle(),
new SonataBlockBundleSonataBlockBundle(),
new KnpBundleMenuBundleKnpMenuBundle(),
new SonataDoctrineORMAdminBundleSonataDoctrineORMAdminBundle(),
new SonataAdminBundleSonataAdminBundle()
];
}
Enable Sonata Admin Bundle
42
@jessicamauerhan | Madison PHP | https://joind.in/16022
Configure Sonata Admin Bundle
#appconfig.yml
sonata_block:
default_contexts: [cms]
blocks:
sonata.admin.block.admin_list:
contexts: [admin]
43
@jessicamauerhan | Madison PHP | https://joind.in/16022
#approuting.yml
admin:
resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
prefix: /admin
_sonata_admin:
resource: .
type: sonata_admin
prefix: /admin
Sonata Admin Bundle Routing
44
@jessicamauerhan | Madison PHP | https://joind.in/16022
$ php app/console assets:install web
$ php app/console cache:clear
Configure Sonata Admin Bundle
45
@jessicamauerhan | Madison PHP | https://joind.in/16022
<?php
namespace DemoAppBundleAdmin;
use SonataAdminBundleAdminAdmin;
use SonataAdminBundleFormFormMapper;
use SonataAdminBundleDatagridListMapper;
use SonataAdminBundleDatagridDatagridMapper;
class CourseAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper){}
protected function configureListFields(ListMapper $listMapper){}
protected function configureDatagridFilters(DatagridMapper
$datagridMapper){}
}
Admin Class
46
@jessicamauerhan | Madison PHP | https://joind.in/16022
Admin Class - Create/Edit Form
// Fields to be shown on create/edit forms
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('title', 'text', ['label' => 'Course Title'])
->add('author', 'entity', ['class' =>
'DemoAppBundleEntityUser'])
->add('description', null, ['required' => false])
->add('categories')
->add('cost');
}
47
@jessicamauerhan | Madison PHP | https://joind.in/16022
Admin Class - List
// Fields to be shown on lists
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('title')
->add('author')
->add('cost')
->add('categories');
}
48
@jessicamauerhan | Madison PHP | https://joind.in/16022
Admin Class - Datagrid Filters
// Fields to be shown on filter forms
protected function configureDatagridFilters(DatagridMapper
$datagridMapper)
{
$datagridMapper
->add('title')
->add('author')
->add('categories');
}
49
@jessicamauerhan | Madison PHP | https://joind.in/16022
Admin Service
# Demo/AppBundle/Resources/config/admin.yml
services:
sonata.admin.course:
class: DemoAppBundleAdminCourseAdmin
tags:
- { name: sonata.admin, manager_type: orm, group:
"Course Management", label: "Courses" }
arguments:
- ~
- DemoAppBundleEntityCourse
- ~
calls:
- [ setTranslationDomain, [AppBundle]]
50
@jessicamauerhan | Madison PHP | https://joind.in/16022
Admin Services - Add to Config
# app/config/config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
- { resource: @AppBundle/Resources/config/admin.yml }
51
@jessicamauerhan | Madison PHP | https://joind.in/16022
Done!
52
@jessicamauerhan | Madison PHP | https://joind.in/16022
Optimization
53
@jessicamauerhan | Madison PHP | https://joind.in/16022
Symfony Debug Toolbar
54
@jessicamauerhan | Madison PHP | https://joind.in/16022
Symfony Profiler
55
@jessicamauerhan | Madison PHP | https://joind.in/16022
Admin Class - Custom Query (List)
public function createQuery($context = 'list')
{
$queryBuilder = $this->getModelManager()
->getEntityManager('modelsCourse')
->createQueryBuilder();
$queryBuilder->select('course', 'categories')
->from('modelsCourse', 'course')
->leftJoin('course.categories', 'categories');
$proxyQuery = new ProxyQuery($queryBuilder);
return $proxyQuery;
}
56
@jessicamauerhan | Madison PHP | https://joind.in/16022
Admin Class - Custom Query (Edit)
Page)public function getObject($id)
{
$dql = "SELECT course, categories
FROM modelsCourse course
LEFT JOIN course.categories categories
WHERE course.id = :id";
$query = $this->getModelManager()
->getEntityManager('modelsCourse')
->createQuery($dql);
$query->setParameter('id', $id);
$course = $query->getOneOrNullResult();
return $course;
}
57
@jessicamauerhan | Madison PHP | https://joind.in/16022
API
58
@jessicamauerhan | Madison PHP | https://joind.in/16022
Install FOS Rest Bundle
$ php composer require "friendsofsymfony/rest-bundle"
$ php composer require "jms/serializer-bundle"
$ php composer require "nelmio/api-doc-bundle"
59
@jessicamauerhan | Madison PHP | https://joind.in/16022
Enable FOS Rest Bundle and Serializer Bundle
//AppKernel.php
public function registerBundles()
{
return [
// ...
new FOSRestBundleRestBundle(),
new JMSSerializerBundleJMSSerializerBundle(),
new NelmioApiDocBundleNelmioApiDocBundle()
];
}
60
@jessicamauerhan | Madison PHP | https://joind.in/16022
Configuration
# app/config/routing.yml
NelmioApiDocBundle:
resource: "@NelmioApiDocBundle/Resources/config/routing.yml"
prefix: /api/doc
# app/config/config.yml
nelmio_api_doc: ~
framework:
templating:
engines: ['twig']
61
@jessicamauerhan | Madison PHP | https://joind.in/16022
API Controller
<?php
namespace DemoAppBundleController;
use FOSRestBundleControllerFOSRestController;
use FOSRestBundleControllerAnnotationsGet;
class UsersController extends FOSRestController
{
/**
* @Get("/users/")
*/
public function getUsersAction()
{
$data = $this->getDoctrine()->getRepository('modelsUser')->findAll();
$view = $this->view($data, 200)
->setTemplate("AppBundle:Basic:json.twig")
->setTemplateVar('users');
return $this->handleView($view);
}
}
62
@jessicamauerhan | Madison PHP | https://joind.in/16022
JSON Twig Tpl
{% spaceless %}{% if json is defined %}
{{ json|json_encode()|raw }}
{% else %}
[]
{% endif %}{% endspaceless %}
63
@jessicamauerhan | Madison PHP | https://joind.in/16022
Add Doc Blocks with Annotation to
API Controller
<?php
namespace DemoAppBundleController;
use ...
class UsersController extends FOSRestController
{
/**
* @Get("/users/")
* @ApiDoc(
* resource=true,
* description="List of Users",
* output="modelsUser"
* )
*/
public function getUsersAction()
{
$data = $this->getDoctrine()->getRepository('modelsUser')->findAll();
$view = $this->view($data, 200)
->setTemplate("AppBundle:Basic:json.twig")
->setTemplateVar('users');
return $this->handleView($view);
}
}
64
@jessicamauerhan | Madison PHP | https://joind.in/16022
Dependency
Injection
Service
Location
Configure
Dependencies
Outside of Class
Class Requests
Dependencies
From Container
65
@jessicamauerhan | Madison PHP | https://joind.in/16022
Service Location
<?php
namespace DemoAppBundleCommand;
use SymfonyBundleFrameworkBundleCommandContainerAwareCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
use DemoAppBundleFactoryMessageFactory;
class UserExportCommand extends ContainerAwareCommand
{
protected function execute(InputInterface $input, OutputInterface $output)
{
$rootDir = $this->getContainer()->getParameter('kernel.root_dir');
$users = $this->getContainer()->getDoctrine()
->getRepository('modelsUser')->findAll();
$messageFactory = new MessageFactory();
foreach ($users as $user) {
$emailMessage = $this->getMessageFactory()->generate($user);
/** More Processing Logic Here */
}
}
}
66
@jessicamauerhan | Madison PHP | https://joind.in/16022
services:
appbundle.message.factory:
class: DemoAppBundleFactoryMessageFactory
Defining Services
67
@jessicamauerhan | Madison PHP | https://joind.in/16022
services:
appbundle.message.factory:
class: DemoAppBundleFactoryMessageFactory
appbundle.repositories.user:
class: DemoAppBundleEntityRepositoriesUserRepository
factory_service: doctrine.orm.entity_manager
factory_method: getRepository
arguments:
- 'modelsUser'
Defining Services
68
@jessicamauerhan | Madison PHP | https://joind.in/16022
services:
appbundle.message.factory:
class: DemoAppBundleFactoryMessageFactory
appbundle.repositories.user:
class: DemoAppBundleEntityRepositoriesUserRepository
factory_service: doctrine.orm.entity_manager
factory_method: getRepository
arguments:
- 'modelsUser'
appbundle.command.user_export:
class: DemoAppBundleCommandUserExportCommand
calls:
- [ setMessageFactory, ["@appbundle.message.factory"]]
- [ setUserRepository, ["@appbundle.repositories.user"]]
- [ setRootDir, ["%kernel.root_dir%"]]
Defining Services
69
@jessicamauerhan | Madison PHP | https://joind.in/16022
Dependency Injection
<?php
namespace DemoAppBundleCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
use DemoAppBundleFactoryMessageFactory;
use SymfonyComponentConsoleCommandCommand;
class UserExportCommand extends Command
{
protected $messageFactory;
protected $rootDir;
protected $userRepository;
}
70
@jessicamauerhan | Madison PHP | https://joind.in/16022
Dependency Injection
<?php
//..
class UserExportCommand extends Command
{
//..
public function setMessageFactory($messageFactory)
{
$this->messageFactory = $messageFactory;
}
public function setRootDir($rootDir)
{
$this->rootDir = $rootDir;
}
public function setUserRepository($userRepository)
{
$this->userRepository = $userRepository;
}
71
@jessicamauerhan | Madison PHP | https://joind.in/16022
Dependency Injection
<?php
//..
class UserExportCommand extends Command
{
//..
public function getMessageFactory()
{
return $this->messageFactory;
}
public function getRootDir()
{
return $this->rootDir;
}
public function getUserRepository()
{
return $this->userRepository;
}
72
@jessicamauerhan | Madison PHP | https://joind.in/16022
Dependency Injection
73
<?php
//..
class UserExportCommand extends Command
{
//..
protected function execute(InputInterface $input, OutputInterface $output)
{
$users = $this->getUserRepository()->findAll();
/** Processing Logic */
foreach ($users as $user) {
$emailMessage = $this->getMessageFactory()->generate($user);
/** More Processing Logic Here */
}
}
}
@jessicamauerhan | Madison PHP | https://joind.in/16022
Controller Using Service Location
74
<?php
namespace DemoAppBundleController;
use FOSRestBundleControllerFOSRestController;
use FOSRestBundleControllerAnnotationsGet;
class UsersController extends FOSRestController
{
/**
* @Get("/users/")
*/
public function getUsersAction()
{
$data = $this->getDoctrine()->getRepository('modelsUser')->findAll();
$view = $this->view($data, 200)
->setTemplate("AppBundle:Basic:json.twig")
->setTemplateVar('users');
return $this->handleView($view);
}
}
@jessicamauerhan | Madison PHP | https://joind.in/16022
services:
appbundle.controllers.users_controller:
class: DemoAppBundleControllerUsersController
calls:
- [ setUserRepository, ["@appbundle.repositories.user"]]
Defining A Controller As A Service
75
@jessicamauerhan | Madison PHP | https://joind.in/16022
Controller using Dependency Injection
76
/**
* @Route(service="appbundle.controllers.users_controller")
*/
class UsersController extends FOSRestController
{
protected $userRepository;
public function setUserRepository($userRepository)
{
$this->userRepository = $userRepository;
}
/**
* @Get("/users/")
*/
public function getUsersAction()
{
$data = $this->userRepository->findAll();
$view = $this->view($data, 200)
->setTemplate("AppBundle:Basic:json.twig")
->setTemplateVar('users');
return $this->handleView($view);
}
}
@jessicamauerhan | Madison PHP | https://joind.in/16022
Current Project
Status
It's stable!
● New Symfony based Admin
was launched after about 9
months (thanks developers!)
● Had a few bugs, took about 3
more months to be stable
● Over past year, close to 0
regressions (Thanks Behat!)
● Very limited downtime
(Thanks Amazon, Elastic
Beanstalk, Aurora!)
77
@jessicamauerhan | Madison PHP | https://joind.in/16022
Technical Goals
Happy Developers!
● Maintainable Code: Check
● Quality Code: Check
● Documentation: Check
● Rapid Development: Oh Yeah
● Easy Deployment: So Easy!
● Zero Regressions: Close
Enough!
78
@jessicamauerhan | Madison PHP | https://joind.in/16022
Rebuilding Our Foundation
How We Used Symfony To
Rewrite Our Application
@jessicamauerhan | Madison PHP | https://joind.in/16022
@jessicamauerhan | Madison PHP | https://joind.in/16022
Resources
Gitflow: https://github.com/nvie/gitflow
PSR-2: http://www.php-fig.org/psr/psr-2/
PHPMD: http://phpmd.org/
Pre-commit hook: https://gist.github.com/jmauerhan/d18e7c232acb3986134d
Doctrine: http://www.doctrine-project.org/
Doctrine Migrations Bundle: http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html
Doctrine Data Fixtures Bundle: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html
Behat: http://docs.behat.org/en/v2.5/
XDebug Wizard: http://xdebug.org/wizard.php
Sonata Project: https://sonata-project.org/bundles/admin/2-3/doc/index.html
FOS Rest Bundle: http://symfony.com/doc/master/bundles/FOSRestBundle/index.html
Nelmio API Doc Bundle: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Resources/doc/index.md
80

More Related Content

What's hot

PAC 2019 virtual Bruno Audoux
PAC 2019 virtual Bruno Audoux PAC 2019 virtual Bruno Audoux
PAC 2019 virtual Bruno Audoux
Neotys
 
M is for modernization
M is for modernizationM is for modernization
M is for modernization
Red Pill Now
 
PAC 2020 Santorin - Ankur Jain
PAC 2020 Santorin - Ankur JainPAC 2020 Santorin - Ankur Jain
PAC 2020 Santorin - Ankur Jain
Neotys
 
Testing Your Code as Part of an Industrial Grade Workflow
Testing Your Code as Part of an Industrial Grade WorkflowTesting Your Code as Part of an Industrial Grade Workflow
Testing Your Code as Part of an Industrial Grade Workflow
Pantheon
 
Capybara testing
Capybara testingCapybara testing
Capybara testing
Futureworkz
 
Gradle Show and Tell
Gradle Show and TellGradle Show and Tell
Gradle Show and Tell
Steve Pember
 
The PRPL Pattern
The PRPL PatternThe PRPL Pattern
The PRPL Pattern
Red Pill Now
 
Agular in a microservices world
Agular in a microservices worldAgular in a microservices world
Agular in a microservices world
Brecht Billiet
 
EuroPython 2011 - How to build complex web applications having fun?
EuroPython 2011 - How to build complex web applications having fun?EuroPython 2011 - How to build complex web applications having fun?
EuroPython 2011 - How to build complex web applications having fun?
Andrew Mleczko
 
React 101
React 101React 101
React 101
AndryRajohnson
 
Dumb and smart components + redux (1)
Dumb and smart components + redux (1)Dumb and smart components + redux (1)
Dumb and smart components + redux (1)
Brecht Billiet
 
Create, test, secure, repeat
Create, test, secure, repeatCreate, test, secure, repeat
Create, test, secure, repeat
Michelangelo van Dam
 
Building reliable applications with React, C#, and Azure
Building reliable applications with React, C#, and AzureBuilding reliable applications with React, C#, and Azure
Building reliable applications with React, C#, and Azure
Maurice De Beijer [MVP]
 
Speed = $$$
Speed = $$$Speed = $$$
Speed = $$$
Peter Gfader
 
JS Fest 2019. Minko Gechev. Building Fast Angular Applications by Default
JS Fest 2019. Minko Gechev. Building Fast Angular Applications by DefaultJS Fest 2019. Minko Gechev. Building Fast Angular Applications by Default
JS Fest 2019. Minko Gechev. Building Fast Angular Applications by Default
JSFestUA
 
Building large and scalable mission critical applications with React
Building large and scalable mission critical applications with ReactBuilding large and scalable mission critical applications with React
Building large and scalable mission critical applications with React
Maurice De Beijer [MVP]
 
Angular2 Upgrade
Angular2 UpgradeAngular2 Upgrade
Eradicate Flaky Tests - AppiumConf 2021
Eradicate Flaky Tests - AppiumConf 2021Eradicate Flaky Tests - AppiumConf 2021
Eradicate Flaky Tests - AppiumConf 2021
Anand Bagmar
 
TestBrowser Driven Development: How to get bulletproof code from lazy developers
TestBrowser Driven Development: How to get bulletproof code from lazy developersTestBrowser Driven Development: How to get bulletproof code from lazy developers
TestBrowser Driven Development: How to get bulletproof code from lazy developers
Dylan Jay
 
WordPress automation and CI
WordPress automation and CIWordPress automation and CI
WordPress automation and CI
Ran Bar-Zik
 

What's hot (20)

PAC 2019 virtual Bruno Audoux
PAC 2019 virtual Bruno Audoux PAC 2019 virtual Bruno Audoux
PAC 2019 virtual Bruno Audoux
 
M is for modernization
M is for modernizationM is for modernization
M is for modernization
 
PAC 2020 Santorin - Ankur Jain
PAC 2020 Santorin - Ankur JainPAC 2020 Santorin - Ankur Jain
PAC 2020 Santorin - Ankur Jain
 
Testing Your Code as Part of an Industrial Grade Workflow
Testing Your Code as Part of an Industrial Grade WorkflowTesting Your Code as Part of an Industrial Grade Workflow
Testing Your Code as Part of an Industrial Grade Workflow
 
Capybara testing
Capybara testingCapybara testing
Capybara testing
 
Gradle Show and Tell
Gradle Show and TellGradle Show and Tell
Gradle Show and Tell
 
The PRPL Pattern
The PRPL PatternThe PRPL Pattern
The PRPL Pattern
 
Agular in a microservices world
Agular in a microservices worldAgular in a microservices world
Agular in a microservices world
 
EuroPython 2011 - How to build complex web applications having fun?
EuroPython 2011 - How to build complex web applications having fun?EuroPython 2011 - How to build complex web applications having fun?
EuroPython 2011 - How to build complex web applications having fun?
 
React 101
React 101React 101
React 101
 
Dumb and smart components + redux (1)
Dumb and smart components + redux (1)Dumb and smart components + redux (1)
Dumb and smart components + redux (1)
 
Create, test, secure, repeat
Create, test, secure, repeatCreate, test, secure, repeat
Create, test, secure, repeat
 
Building reliable applications with React, C#, and Azure
Building reliable applications with React, C#, and AzureBuilding reliable applications with React, C#, and Azure
Building reliable applications with React, C#, and Azure
 
Speed = $$$
Speed = $$$Speed = $$$
Speed = $$$
 
JS Fest 2019. Minko Gechev. Building Fast Angular Applications by Default
JS Fest 2019. Minko Gechev. Building Fast Angular Applications by DefaultJS Fest 2019. Minko Gechev. Building Fast Angular Applications by Default
JS Fest 2019. Minko Gechev. Building Fast Angular Applications by Default
 
Building large and scalable mission critical applications with React
Building large and scalable mission critical applications with ReactBuilding large and scalable mission critical applications with React
Building large and scalable mission critical applications with React
 
Angular2 Upgrade
Angular2 UpgradeAngular2 Upgrade
Angular2 Upgrade
 
Eradicate Flaky Tests - AppiumConf 2021
Eradicate Flaky Tests - AppiumConf 2021Eradicate Flaky Tests - AppiumConf 2021
Eradicate Flaky Tests - AppiumConf 2021
 
TestBrowser Driven Development: How to get bulletproof code from lazy developers
TestBrowser Driven Development: How to get bulletproof code from lazy developersTestBrowser Driven Development: How to get bulletproof code from lazy developers
TestBrowser Driven Development: How to get bulletproof code from lazy developers
 
WordPress automation and CI
WordPress automation and CIWordPress automation and CI
WordPress automation and CI
 

Viewers also liked

How to Serve a Volleyball
How to Serve a VolleyballHow to Serve a Volleyball
How to Serve a Volleyball
sinahudson
 
Taller recuperación desarrollo humano
Taller recuperación desarrollo humanoTaller recuperación desarrollo humano
Taller recuperación desarrollo humano
Jorge Hernan Arcila Aristizabal
 
All Canada Show 2009
All Canada Show 2009All Canada Show 2009
All Canada Show 2009
kaylah123
 
State Enterprise Reform In Zimbabwe NRZ
State Enterprise Reform In Zimbabwe NRZState Enterprise Reform In Zimbabwe NRZ
State Enterprise Reform In Zimbabwe NRZMish S Dube
 
Sharing economy - Tecnologia e confiança
Sharing economy - Tecnologia e confiançaSharing economy - Tecnologia e confiança
Sharing economy - Tecnologia e confiança
ASAAS
 
Compras Públicas - Parte I - Introdução
Compras Públicas - Parte I - IntroduçãoCompras Públicas - Parte I - Introdução
Compras Públicas - Parte I - Introdução
Rafael Lisboa
 
A Beginner's Guide To Twitter For Business
A Beginner's Guide To Twitter For BusinessA Beginner's Guide To Twitter For Business
A Beginner's Guide To Twitter For Business
Diarmuid Latimer
 
Taller de recuperaci_n_grado_sexto_2016_
Taller de recuperaci_n_grado_sexto_2016_Taller de recuperaci_n_grado_sexto_2016_
Taller de recuperaci_n_grado_sexto_2016_
Jorge Hernan Arcila Aristizabal
 
Cpa ex узкийкруг_cpa_310316
Cpa ex узкийкруг_cpa_310316Cpa ex узкийкруг_cpa_310316
Cpa ex узкийкруг_cpa_310316
CPAex
 
покупки с мобильных устройств (Google russia)
покупки с мобильных устройств (Google russia)покупки с мобильных устройств (Google russia)
покупки с мобильных устройств (Google russia)
CPAex
 
Misa con niños 3 15 de enero
Misa con niños 3  15 de eneroMisa con niños 3  15 de enero
Misa con niños 3 15 de enero
Franciscanos Valladolid
 
Introduction to Java Programming
Introduction to Java ProgrammingIntroduction to Java Programming
Introduction to Java Programming
Pokequesthero
 

Viewers also liked (13)

Ac 104 - copy
Ac 104 - copyAc 104 - copy
Ac 104 - copy
 
How to Serve a Volleyball
How to Serve a VolleyballHow to Serve a Volleyball
How to Serve a Volleyball
 
Taller recuperación desarrollo humano
Taller recuperación desarrollo humanoTaller recuperación desarrollo humano
Taller recuperación desarrollo humano
 
All Canada Show 2009
All Canada Show 2009All Canada Show 2009
All Canada Show 2009
 
State Enterprise Reform In Zimbabwe NRZ
State Enterprise Reform In Zimbabwe NRZState Enterprise Reform In Zimbabwe NRZ
State Enterprise Reform In Zimbabwe NRZ
 
Sharing economy - Tecnologia e confiança
Sharing economy - Tecnologia e confiançaSharing economy - Tecnologia e confiança
Sharing economy - Tecnologia e confiança
 
Compras Públicas - Parte I - Introdução
Compras Públicas - Parte I - IntroduçãoCompras Públicas - Parte I - Introdução
Compras Públicas - Parte I - Introdução
 
A Beginner's Guide To Twitter For Business
A Beginner's Guide To Twitter For BusinessA Beginner's Guide To Twitter For Business
A Beginner's Guide To Twitter For Business
 
Taller de recuperaci_n_grado_sexto_2016_
Taller de recuperaci_n_grado_sexto_2016_Taller de recuperaci_n_grado_sexto_2016_
Taller de recuperaci_n_grado_sexto_2016_
 
Cpa ex узкийкруг_cpa_310316
Cpa ex узкийкруг_cpa_310316Cpa ex узкийкруг_cpa_310316
Cpa ex узкийкруг_cpa_310316
 
покупки с мобильных устройств (Google russia)
покупки с мобильных устройств (Google russia)покупки с мобильных устройств (Google russia)
покупки с мобильных устройств (Google russia)
 
Misa con niños 3 15 de enero
Misa con niños 3  15 de eneroMisa con niños 3  15 de enero
Misa con niños 3 15 de enero
 
Introduction to Java Programming
Introduction to Java ProgrammingIntroduction to Java Programming
Introduction to Java Programming
 

Similar to Rebuilding our Foundation

Fundamentals of Extending Magento 2 - php[world] 2015
Fundamentals of Extending Magento 2 - php[world] 2015Fundamentals of Extending Magento 2 - php[world] 2015
Fundamentals of Extending Magento 2 - php[world] 2015
David Alger
 
Improving QA on PHP projects - confoo 2011
Improving QA on PHP projects - confoo 2011Improving QA on PHP projects - confoo 2011
Improving QA on PHP projects - confoo 2011
Michelangelo van Dam
 
PHP Development Tools
PHP  Development ToolsPHP  Development Tools
PHP Development Tools
Antony Abramchenko
 
Php Power Tools
Php Power ToolsPhp Power Tools
Php Power Tools
Michelangelo van Dam
 
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit SoftwaretestsEffizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
DECK36
 
Doctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperDoctrine Php Object Relational Mapper
Doctrine Php Object Relational Mapper
Jonathan Wage
 
Standards: Don't pee in the pool
Standards: Don't pee in the poolStandards: Don't pee in the pool
Standards: Don't pee in the pool
David Yell
 
PHP CE 2018 - Building Symfony application with Ports and Adapters approach a...
PHP CE 2018 - Building Symfony application with Ports and Adapters approach a...PHP CE 2018 - Building Symfony application with Ports and Adapters approach a...
PHP CE 2018 - Building Symfony application with Ports and Adapters approach a...
Dariusz Drobisz
 
PHP QA Tools
PHP QA ToolsPHP QA Tools
PHP QA Tools
rjsmelo
 
Simplify your professional web development with symfony
Simplify your professional web development with symfonySimplify your professional web development with symfony
Simplify your professional web development with symfony
Francois Zaninotto
 
Symfony2 for Midgard Developers
Symfony2 for Midgard DevelopersSymfony2 for Midgard Developers
Symfony2 for Midgard Developers
Henri Bergius
 
MVC = Make Venerated Code?
MVC = Make Venerated Code?MVC = Make Venerated Code?
MVC = Make Venerated Code?
Patrick Allaert
 
Tech 2 - Introduction to the Code
Tech 2 - Introduction to the CodeTech 2 - Introduction to the Code
Tech 2 - Introduction to the CodeAidIQ
 
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
Pantheon
 
JSFest 2019: Technology agnostic microservices at SPA frontend
JSFest 2019: Technology agnostic microservices at SPA frontendJSFest 2019: Technology agnostic microservices at SPA frontend
JSFest 2019: Technology agnostic microservices at SPA frontend
Vlad Fedosov
 
Edp bootstrapping a-software_company
Edp bootstrapping a-software_companyEdp bootstrapping a-software_company
Edp bootstrapping a-software_companyGanesh Kulkarni
 
Workshop: Refactoring Legacy PHP: The Complete Guide
Workshop: Refactoring Legacy PHP: The Complete Guide Workshop: Refactoring Legacy PHP: The Complete Guide
Workshop: Refactoring Legacy PHP: The Complete Guide
Junade Ali
 
Benefit of CodeIgniter php framework
Benefit of CodeIgniter php frameworkBenefit of CodeIgniter php framework
Benefit of CodeIgniter php frameworkBo-Yi Wu
 
Effective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 DevelopersEffective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 Developers
Marcin Chwedziak
 
Joomla Code Quality Control and Automation Testing
Joomla Code Quality Control and Automation TestingJoomla Code Quality Control and Automation Testing
Joomla Code Quality Control and Automation Testing
Shyam Sunder Verma
 

Similar to Rebuilding our Foundation (20)

Fundamentals of Extending Magento 2 - php[world] 2015
Fundamentals of Extending Magento 2 - php[world] 2015Fundamentals of Extending Magento 2 - php[world] 2015
Fundamentals of Extending Magento 2 - php[world] 2015
 
Improving QA on PHP projects - confoo 2011
Improving QA on PHP projects - confoo 2011Improving QA on PHP projects - confoo 2011
Improving QA on PHP projects - confoo 2011
 
PHP Development Tools
PHP  Development ToolsPHP  Development Tools
PHP Development Tools
 
Php Power Tools
Php Power ToolsPhp Power Tools
Php Power Tools
 
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit SoftwaretestsEffizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
 
Doctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperDoctrine Php Object Relational Mapper
Doctrine Php Object Relational Mapper
 
Standards: Don't pee in the pool
Standards: Don't pee in the poolStandards: Don't pee in the pool
Standards: Don't pee in the pool
 
PHP CE 2018 - Building Symfony application with Ports and Adapters approach a...
PHP CE 2018 - Building Symfony application with Ports and Adapters approach a...PHP CE 2018 - Building Symfony application with Ports and Adapters approach a...
PHP CE 2018 - Building Symfony application with Ports and Adapters approach a...
 
PHP QA Tools
PHP QA ToolsPHP QA Tools
PHP QA Tools
 
Simplify your professional web development with symfony
Simplify your professional web development with symfonySimplify your professional web development with symfony
Simplify your professional web development with symfony
 
Symfony2 for Midgard Developers
Symfony2 for Midgard DevelopersSymfony2 for Midgard Developers
Symfony2 for Midgard Developers
 
MVC = Make Venerated Code?
MVC = Make Venerated Code?MVC = Make Venerated Code?
MVC = Make Venerated Code?
 
Tech 2 - Introduction to the Code
Tech 2 - Introduction to the CodeTech 2 - Introduction to the Code
Tech 2 - Introduction to the Code
 
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
 
JSFest 2019: Technology agnostic microservices at SPA frontend
JSFest 2019: Technology agnostic microservices at SPA frontendJSFest 2019: Technology agnostic microservices at SPA frontend
JSFest 2019: Technology agnostic microservices at SPA frontend
 
Edp bootstrapping a-software_company
Edp bootstrapping a-software_companyEdp bootstrapping a-software_company
Edp bootstrapping a-software_company
 
Workshop: Refactoring Legacy PHP: The Complete Guide
Workshop: Refactoring Legacy PHP: The Complete Guide Workshop: Refactoring Legacy PHP: The Complete Guide
Workshop: Refactoring Legacy PHP: The Complete Guide
 
Benefit of CodeIgniter php framework
Benefit of CodeIgniter php frameworkBenefit of CodeIgniter php framework
Benefit of CodeIgniter php framework
 
Effective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 DevelopersEffective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 Developers
 
Joomla Code Quality Control and Automation Testing
Joomla Code Quality Control and Automation TestingJoomla Code Quality Control and Automation Testing
Joomla Code Quality Control and Automation Testing
 

More from Jessica Mauerhan

SOLID in Practice
SOLID in PracticeSOLID in Practice
SOLID in Practice
Jessica Mauerhan
 
Double Loop: TDD & BDD Done Right
Double Loop: TDD & BDD Done RightDouble Loop: TDD & BDD Done Right
Double Loop: TDD & BDD Done Right
Jessica Mauerhan
 
Solid in practice
Solid in practiceSolid in practice
Solid in practice
Jessica Mauerhan
 
Double Loop: TDD & BDD Done Right!
Double Loop: TDD & BDD Done Right!Double Loop: TDD & BDD Done Right!
Double Loop: TDD & BDD Done Right!
Jessica Mauerhan
 
Double Loop
Double LoopDouble Loop
Double Loop
Jessica Mauerhan
 
Behat - Beyond the Basics (2016 - SunshinePHP)
Behat - Beyond the Basics (2016 - SunshinePHP)Behat - Beyond the Basics (2016 - SunshinePHP)
Behat - Beyond the Basics (2016 - SunshinePHP)
Jessica Mauerhan
 
Behat: Beyond the Basics
Behat: Beyond the BasicsBehat: Beyond the Basics
Behat: Beyond the Basics
Jessica Mauerhan
 

More from Jessica Mauerhan (7)

SOLID in Practice
SOLID in PracticeSOLID in Practice
SOLID in Practice
 
Double Loop: TDD & BDD Done Right
Double Loop: TDD & BDD Done RightDouble Loop: TDD & BDD Done Right
Double Loop: TDD & BDD Done Right
 
Solid in practice
Solid in practiceSolid in practice
Solid in practice
 
Double Loop: TDD & BDD Done Right!
Double Loop: TDD & BDD Done Right!Double Loop: TDD & BDD Done Right!
Double Loop: TDD & BDD Done Right!
 
Double Loop
Double LoopDouble Loop
Double Loop
 
Behat - Beyond the Basics (2016 - SunshinePHP)
Behat - Beyond the Basics (2016 - SunshinePHP)Behat - Beyond the Basics (2016 - SunshinePHP)
Behat - Beyond the Basics (2016 - SunshinePHP)
 
Behat: Beyond the Basics
Behat: Beyond the BasicsBehat: Beyond the Basics
Behat: Beyond the Basics
 

Recently uploaded

Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Albert Hoitingh
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
RTTS
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
Product School
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
DianaGray10
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Product School
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
ControlCase
 
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
Paul Groth
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
Elena Simperl
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Product School
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Ramesh Iyer
 
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdfFIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
Dorra BARTAGUIZ
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
James Anderson
 

Recently uploaded (20)

Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
 
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
 
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdfFIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
 

Rebuilding our Foundation

  • 1. @jessicamauerhan | Madison PHP | https://joind.in/16022 Rebuilding Our Foundation How We Used Symfony To Rewrite Our Application @jessicamauerhan | Madison PHP | https://joind.in/16022
  • 2. @jessicamauerhan | Madison PHP | https://joind.in/16022 ● Project History ● Problems and Goals ● Why Symfony? ● Rapid Development of Quality Code ● Doctrine DBAL / ORM Topics ● Automated Testing ● Sonata Admin Bundle ● Creating an API with Symfony ● Dependency Injection ● Current Project Status 2
  • 3. @jessicamauerhan | Madison PHP | https://joind.in/16022 ● Learning Management System ● Content Production ● E-Commerce ● Business to Business (B2B) Application Summary 3
  • 4. @jessicamauerhan | Madison PHP | https://joind.in/16022 Problematic History ● Broken Admin Panel ● No Documentation of Basic Processes ● Frontend Site Worked, Progress Stalled ● Complex Logic not Documented 4
  • 5. @jessicamauerhan | Madison PHP | https://joind.in/16022 Business Goals ● Add Missing Admin Panel Functionality ● Add New Features Without Breaking Existing Features ● Avoid Downtime 5
  • 6. @jessicamauerhan | Madison PHP | https://joind.in/16022 Technical Goals ● Maintainable Code ● Quality Code ● Documentation ● Rapid Development ● Easy Deployment ● Zero Regressions Per Release 6
  • 7. @jessicamauerhan | Madison PHP | https://joind.in/16022 Why Symfony? ● Community ○ Third Party Code Integration ○ Blazing Trails ○ Popularity ○ Support 7
  • 8. @jessicamauerhan | Madison PHP | https://joind.in/16022 Why Symfony? ● Technology ○ Dependency Injection & Decoupling ○ Unit Testing ○ Functional Testing ○ Behavior Testing 8
  • 9. @jessicamauerhan | Madison PHP | https://joind.in/16022 Rapid Development of Quality Code ● Version Control: Git ● Development Workflow 9
  • 10. @jessicamauerhan | Madison PHP | https://joind.in/16022 Image sourced from Atlassian: https://www.atlassian.com/git/tutorials/comparing- workflows/gitflow-workflow Licensed under the Creative Commons Attribution 2.5 Australia License. 10
  • 11. @jessicamauerhan | Madison PHP | https://joind.in/16022 Rapid Development of Quality Code ● Version Control: Git ● Development Workflow: Gitflow ● Code Style Guide: PSR-2 11
  • 12. @jessicamauerhan | Madison PHP | https://joind.in/16022 Rapid Development of Quality Code ● Version Control: Git ● Development Workflow: Gitflow ● Code Style Guide: PSR-2 ● Code Quality Rules 12
  • 13. @jessicamauerhan | Madison PHP | https://joind.in/16022 PHP Mess Detector $ php composer require "phpmd/phpmd" --dev $ php composer install --no-dev 13
  • 14. @jessicamauerhan | Madison PHP | https://joind.in/16022 PHP Mess Detector Rules <?xml version="1.0"?> <ruleset name="Code Quality" xmlns="http://pmd.sf.net/ruleset/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:noNamespaceSchemaLocation=" http://pmd.sf.net/ruleset_xml_schema.xsd"> <description>Custom Code Quality Rules</description> <!--Rulesets: http://phpmd.org/rules/index.html--> <rule ref="rulesets/cleancode.xml"/> <rule ref="rulesets/naming.xml/ShortVariable"> <properties> <property name="minimum" value="4"/> </properties> </rule> </ruleset> 14
  • 15. @jessicamauerhan | Madison PHP | https://joind.in/16022 Rapid Development of Quality Code ● Version Control ● Development Workflow: Gitflow ● Code Style Guide: PSR-2 ● Code Quality Rules: PHP Mess Detector ● Code Quality Enforcement 15
  • 16. @jessicamauerhan | Madison PHP | https://joind.in/16022 Git Pre-Commit Code Quality Hook #!/usr/bin/env php <?php require __DIR__ . '/../../vendor/autoload.php'; use SymfonyComponentConsoleApplication; class CodeQualityTool extends Application { private $projectRoot; const PHP_FILES_IN_SRC = '/^src/(.*)(.php)$/'; const PHP_FILES_IN_APPLICATION = '/^application/(.*)(.php)$/'; public function __construct() { $this->projectRoot = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR); parent::__construct('Code Quality Tool', '1.0.0'); } } 16
  • 17. @jessicamauerhan | Madison PHP | https://joind.in/16022 Git Pre-Commit Code Quality Hook private function extractCommitedFiles() { $files = []; $output = []; exec("git diff --cached --name-status --diff-filter=ACM", $output); foreach ($output as $line) { $filename = trim(substr($line, 1)); $isAppFile = preg_match(self::PHP_FILES_IN_APPLICATION, $filename); $isSrcFile = preg_match(self::PHP_FILES_IN_SRC, $filename); if ($isAppFile || $isSrcFile) { $files[] = $filename; } } return $files; } 17
  • 18. @jessicamauerhan | Madison PHP | https://joind.in/16022 Git Pre-Commit Code Quality Hook private function checkPhpMd($files) { $succeed = true; foreach ($files as $file) { $processArgs = ['bin/phpmd', $file, 'text', 'phpmd-rules.xml']; $processBuilder = new ProcessBuilder($processArgs); $processBuilder->setWorkingDirectory($this->projectRoot); $process = $processBuilder->getProcess(); $process->run(); if (!$process->isSuccessful()) { $this->output->writeln($file); $this->output->writeln($process->getErrorOutput()); $this->output->writeln($process->getOutput()); $succeed = false; } } return $succeed; } 18
  • 19. @jessicamauerhan | Madison PHP | https://joind.in/16022 Git Pre-Commit Code Quality Hook public function doRun(InputInterface $input, OutputInterface $output) { $this->input = $input; $this->output = $output; $output->writeln('<info>Fetching files</info>'); $files = $this->extractCommitedFiles(); $info = '<info>Checking for messy code with PHPMD</info>'; $output->writeln($info); if (!$this->checkPhpMd($files)) { throw new Exception(sprintf('There are PHPMD violations!')); } } 19
  • 20. @jessicamauerhan | Madison PHP | https://joind.in/16022 Rapid Development of Quality Code ● Version Control: Git ● Development Workflow: Gitflow ● Code Style Guide: PSR-2 ● Code Quality Rules: PHP Mess Detector ● Code Quality Enforcement: Git Hooks 20
  • 21. @jessicamauerhan | Madison PHP | https://joind.in/16022 Doctrine 21
  • 22. @jessicamauerhan | Madison PHP | https://joind.in/16022 Doctrine DBAL / ORM ● Database Abstraction Layer (DBAL) ○ Database Vendor Agnostic ○ Query Builder ● Object Relational Mapper (ORM) ○ Maps Objects to Tables, Properties to Fields ○ Object Relationships ○ DQL - it's like SQL for your Objects 22
  • 23. @jessicamauerhan | Madison PHP | https://joind.in/16022 Generating Entity Classes (Annotations) $ php app/console doctrine:mapping:import --force AcmeBlogBundle xml $ php app/console doctrine:mapping:convert annotation ./src $ php app/console doctrine:generate:entities AcmeBlogBundle 23
  • 24. @jessicamauerhan | Madison PHP | https://joind.in/16022 Annotated Entity Class <?php namespace AcmeBlogBundleEntity; use DoctrineORMMapping as ORM; /** * AcmeBlogBundleEntityBlogComment * * @ORMTable(name="blog_comment") * @ORMEntity */ class BlogComment { /** * @var integer $id * @ORMColumn(name="id", type="bigint") * @ORMId * @ORMGeneratedValue(strategy="IDENTITY") */ private $id; /** * @var string $author * @ORMColumn(name="author", type="string", length=100, nullable=false) */ private $author; } 24
  • 25. @jessicamauerhan | Madison PHP | https://joind.in/16022 Other Doctrine Tools ● Doctrine Migrations ● Data Fixtures Library 25
  • 26. @jessicamauerhan | Madison PHP | https://joind.in/16022 Introducing Automated Tests 26
  • 27. @jessicamauerhan | Madison PHP | https://joind.in/16022 $ php composer require "behat/behat" --dev Installing Behat 27
  • 28. @jessicamauerhan | Madison PHP | https://joind.in/16022 Writing Features for Existing Code ● Write Feature ● Run Test ○ Test Passes - Double Check ○ Test Fails ■ Described Feature Wrong ■ Mistake in Test Code ■ Feature is Broken 28
  • 29. @jessicamauerhan | Madison PHP | https://joind.in/16022 New Symfony Development 29
  • 30. @jessicamauerhan | Madison PHP | https://joind.in/16022 Installing Symfony Framework 30
  • 31. @jessicamauerhan | Madison PHP | https://joind.in/16022 Installing Symfony Framework 31
  • 32. @jessicamauerhan | Madison PHP | https://joind.in/16022 Merging Symfony Framework with Existing Code ● Install Separately ● Move Directories ● Combine composer.json ○ scripts ○ extra ○ autoload 32
  • 33. @jessicamauerhan | Madison PHP | https://joind.in/16022 Generate App Bundle 33
  • 34. @jessicamauerhan | Madison PHP | https://joind.in/16022 Generate App Bundle C:wampwwwtest>php appconsole generate:bundle Welcome to the Symfony2 bundle generator Your application code must be written in bundles. This command helps you generate them easily. Each bundle is hosted under a namespace (like Acme/Bundle/BlogBundle). The namespace should begin with a "vendor" name like your company name, your project name, or your client name, followed by one or more optional category sub-namespaces, and it should end with the bundle name itself (which must have Bundle as a suffix). See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#index-1 for more details on bundle naming conventions. Use / instead of for the namespace delimiter to avoid any problem. Bundle namespace: Demo/AppBundle 34
  • 35. @jessicamauerhan | Madison PHP | https://joind.in/16022 Generate App Bundle In your code, a bundle is often referenced by its name. It can be the concatenation of all namespace parts but it's really up to you to come up with a unique name (a good practice is to start with the vendor name). Based on the namespace, we suggest DemoAppBundle. Bundle name [DemoAppBundle]: AppBundle 35
  • 36. @jessicamauerhan | Madison PHP | https://joind.in/16022 The bundle can be generated anywhere. The suggested default directory uses the standard conventions. Target directory [C:wampwwwtest/src]: Determine the format to use for the generated configuration. Configuration format (yml, xml, php, or annotation): annotation To help you get started faster, the command can generate some code snippets for you. Do you want to generate the whole directory structure [no]? yes Generate App Bundle 36
  • 37. @jessicamauerhan | Madison PHP | https://joind.in/16022 Generate App Bundle Summary before generation You are going to generate a "DemoAppBundleAppBundle" bundle in "C:wampwwwtest/src/" using the "annotation" format. Do you confirm generation [yes]? Bundle generation Generating the bundle code: OK Checking that the bundle is autoloaded: OK Confirm automatic update of your Kernel [yes]? Enabling the bundle inside the Kernel: OK Confirm automatic update of the Routing [yes]? Importing the bundle routing resource: OK You can now start using the generated code! 37
  • 38. @jessicamauerhan | Madison PHP | https://joind.in/16022 Sonata Admin 38
  • 39. @jessicamauerhan | Madison PHP | https://joind.in/16022 39
  • 40. @jessicamauerhan | Madison PHP | https://joind.in/16022 40
  • 41. @jessicamauerhan | Madison PHP | https://joind.in/16022 $ php composer require "sonata-project/admin-bundle" $ php composer require "sonata-project/doctrine-orm-admin-bundle" Install Sonata Admin 41
  • 42. @jessicamauerhan | Madison PHP | https://joind.in/16022 //AppKernel.php public function registerBundles() { return [ //... new SymfonyBundleSecurityBundleSecurityBundle(), new SonataCoreBundleSonataCoreBundle(), new SonataBlockBundleSonataBlockBundle(), new KnpBundleMenuBundleKnpMenuBundle(), new SonataDoctrineORMAdminBundleSonataDoctrineORMAdminBundle(), new SonataAdminBundleSonataAdminBundle() ]; } Enable Sonata Admin Bundle 42
  • 43. @jessicamauerhan | Madison PHP | https://joind.in/16022 Configure Sonata Admin Bundle #appconfig.yml sonata_block: default_contexts: [cms] blocks: sonata.admin.block.admin_list: contexts: [admin] 43
  • 44. @jessicamauerhan | Madison PHP | https://joind.in/16022 #approuting.yml admin: resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml' prefix: /admin _sonata_admin: resource: . type: sonata_admin prefix: /admin Sonata Admin Bundle Routing 44
  • 45. @jessicamauerhan | Madison PHP | https://joind.in/16022 $ php app/console assets:install web $ php app/console cache:clear Configure Sonata Admin Bundle 45
  • 46. @jessicamauerhan | Madison PHP | https://joind.in/16022 <?php namespace DemoAppBundleAdmin; use SonataAdminBundleAdminAdmin; use SonataAdminBundleFormFormMapper; use SonataAdminBundleDatagridListMapper; use SonataAdminBundleDatagridDatagridMapper; class CourseAdmin extends Admin { protected function configureFormFields(FormMapper $formMapper){} protected function configureListFields(ListMapper $listMapper){} protected function configureDatagridFilters(DatagridMapper $datagridMapper){} } Admin Class 46
  • 47. @jessicamauerhan | Madison PHP | https://joind.in/16022 Admin Class - Create/Edit Form // Fields to be shown on create/edit forms protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('title', 'text', ['label' => 'Course Title']) ->add('author', 'entity', ['class' => 'DemoAppBundleEntityUser']) ->add('description', null, ['required' => false]) ->add('categories') ->add('cost'); } 47
  • 48. @jessicamauerhan | Madison PHP | https://joind.in/16022 Admin Class - List // Fields to be shown on lists protected function configureListFields(ListMapper $listMapper) { $listMapper ->addIdentifier('title') ->add('author') ->add('cost') ->add('categories'); } 48
  • 49. @jessicamauerhan | Madison PHP | https://joind.in/16022 Admin Class - Datagrid Filters // Fields to be shown on filter forms protected function configureDatagridFilters(DatagridMapper $datagridMapper) { $datagridMapper ->add('title') ->add('author') ->add('categories'); } 49
  • 50. @jessicamauerhan | Madison PHP | https://joind.in/16022 Admin Service # Demo/AppBundle/Resources/config/admin.yml services: sonata.admin.course: class: DemoAppBundleAdminCourseAdmin tags: - { name: sonata.admin, manager_type: orm, group: "Course Management", label: "Courses" } arguments: - ~ - DemoAppBundleEntityCourse - ~ calls: - [ setTranslationDomain, [AppBundle]] 50
  • 51. @jessicamauerhan | Madison PHP | https://joind.in/16022 Admin Services - Add to Config # app/config/config.yml imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: services.yml } - { resource: @AppBundle/Resources/config/admin.yml } 51
  • 52. @jessicamauerhan | Madison PHP | https://joind.in/16022 Done! 52
  • 53. @jessicamauerhan | Madison PHP | https://joind.in/16022 Optimization 53
  • 54. @jessicamauerhan | Madison PHP | https://joind.in/16022 Symfony Debug Toolbar 54
  • 55. @jessicamauerhan | Madison PHP | https://joind.in/16022 Symfony Profiler 55
  • 56. @jessicamauerhan | Madison PHP | https://joind.in/16022 Admin Class - Custom Query (List) public function createQuery($context = 'list') { $queryBuilder = $this->getModelManager() ->getEntityManager('modelsCourse') ->createQueryBuilder(); $queryBuilder->select('course', 'categories') ->from('modelsCourse', 'course') ->leftJoin('course.categories', 'categories'); $proxyQuery = new ProxyQuery($queryBuilder); return $proxyQuery; } 56
  • 57. @jessicamauerhan | Madison PHP | https://joind.in/16022 Admin Class - Custom Query (Edit) Page)public function getObject($id) { $dql = "SELECT course, categories FROM modelsCourse course LEFT JOIN course.categories categories WHERE course.id = :id"; $query = $this->getModelManager() ->getEntityManager('modelsCourse') ->createQuery($dql); $query->setParameter('id', $id); $course = $query->getOneOrNullResult(); return $course; } 57
  • 58. @jessicamauerhan | Madison PHP | https://joind.in/16022 API 58
  • 59. @jessicamauerhan | Madison PHP | https://joind.in/16022 Install FOS Rest Bundle $ php composer require "friendsofsymfony/rest-bundle" $ php composer require "jms/serializer-bundle" $ php composer require "nelmio/api-doc-bundle" 59
  • 60. @jessicamauerhan | Madison PHP | https://joind.in/16022 Enable FOS Rest Bundle and Serializer Bundle //AppKernel.php public function registerBundles() { return [ // ... new FOSRestBundleRestBundle(), new JMSSerializerBundleJMSSerializerBundle(), new NelmioApiDocBundleNelmioApiDocBundle() ]; } 60
  • 61. @jessicamauerhan | Madison PHP | https://joind.in/16022 Configuration # app/config/routing.yml NelmioApiDocBundle: resource: "@NelmioApiDocBundle/Resources/config/routing.yml" prefix: /api/doc # app/config/config.yml nelmio_api_doc: ~ framework: templating: engines: ['twig'] 61
  • 62. @jessicamauerhan | Madison PHP | https://joind.in/16022 API Controller <?php namespace DemoAppBundleController; use FOSRestBundleControllerFOSRestController; use FOSRestBundleControllerAnnotationsGet; class UsersController extends FOSRestController { /** * @Get("/users/") */ public function getUsersAction() { $data = $this->getDoctrine()->getRepository('modelsUser')->findAll(); $view = $this->view($data, 200) ->setTemplate("AppBundle:Basic:json.twig") ->setTemplateVar('users'); return $this->handleView($view); } } 62
  • 63. @jessicamauerhan | Madison PHP | https://joind.in/16022 JSON Twig Tpl {% spaceless %}{% if json is defined %} {{ json|json_encode()|raw }} {% else %} [] {% endif %}{% endspaceless %} 63
  • 64. @jessicamauerhan | Madison PHP | https://joind.in/16022 Add Doc Blocks with Annotation to API Controller <?php namespace DemoAppBundleController; use ... class UsersController extends FOSRestController { /** * @Get("/users/") * @ApiDoc( * resource=true, * description="List of Users", * output="modelsUser" * ) */ public function getUsersAction() { $data = $this->getDoctrine()->getRepository('modelsUser')->findAll(); $view = $this->view($data, 200) ->setTemplate("AppBundle:Basic:json.twig") ->setTemplateVar('users'); return $this->handleView($view); } } 64
  • 65. @jessicamauerhan | Madison PHP | https://joind.in/16022 Dependency Injection Service Location Configure Dependencies Outside of Class Class Requests Dependencies From Container 65
  • 66. @jessicamauerhan | Madison PHP | https://joind.in/16022 Service Location <?php namespace DemoAppBundleCommand; use SymfonyBundleFrameworkBundleCommandContainerAwareCommand; use SymfonyComponentConsoleInputInputInterface; use SymfonyComponentConsoleOutputOutputInterface; use DemoAppBundleFactoryMessageFactory; class UserExportCommand extends ContainerAwareCommand { protected function execute(InputInterface $input, OutputInterface $output) { $rootDir = $this->getContainer()->getParameter('kernel.root_dir'); $users = $this->getContainer()->getDoctrine() ->getRepository('modelsUser')->findAll(); $messageFactory = new MessageFactory(); foreach ($users as $user) { $emailMessage = $this->getMessageFactory()->generate($user); /** More Processing Logic Here */ } } } 66
  • 67. @jessicamauerhan | Madison PHP | https://joind.in/16022 services: appbundle.message.factory: class: DemoAppBundleFactoryMessageFactory Defining Services 67
  • 68. @jessicamauerhan | Madison PHP | https://joind.in/16022 services: appbundle.message.factory: class: DemoAppBundleFactoryMessageFactory appbundle.repositories.user: class: DemoAppBundleEntityRepositoriesUserRepository factory_service: doctrine.orm.entity_manager factory_method: getRepository arguments: - 'modelsUser' Defining Services 68
  • 69. @jessicamauerhan | Madison PHP | https://joind.in/16022 services: appbundle.message.factory: class: DemoAppBundleFactoryMessageFactory appbundle.repositories.user: class: DemoAppBundleEntityRepositoriesUserRepository factory_service: doctrine.orm.entity_manager factory_method: getRepository arguments: - 'modelsUser' appbundle.command.user_export: class: DemoAppBundleCommandUserExportCommand calls: - [ setMessageFactory, ["@appbundle.message.factory"]] - [ setUserRepository, ["@appbundle.repositories.user"]] - [ setRootDir, ["%kernel.root_dir%"]] Defining Services 69
  • 70. @jessicamauerhan | Madison PHP | https://joind.in/16022 Dependency Injection <?php namespace DemoAppBundleCommand; use SymfonyComponentConsoleInputInputInterface; use SymfonyComponentConsoleOutputOutputInterface; use DemoAppBundleFactoryMessageFactory; use SymfonyComponentConsoleCommandCommand; class UserExportCommand extends Command { protected $messageFactory; protected $rootDir; protected $userRepository; } 70
  • 71. @jessicamauerhan | Madison PHP | https://joind.in/16022 Dependency Injection <?php //.. class UserExportCommand extends Command { //.. public function setMessageFactory($messageFactory) { $this->messageFactory = $messageFactory; } public function setRootDir($rootDir) { $this->rootDir = $rootDir; } public function setUserRepository($userRepository) { $this->userRepository = $userRepository; } 71
  • 72. @jessicamauerhan | Madison PHP | https://joind.in/16022 Dependency Injection <?php //.. class UserExportCommand extends Command { //.. public function getMessageFactory() { return $this->messageFactory; } public function getRootDir() { return $this->rootDir; } public function getUserRepository() { return $this->userRepository; } 72
  • 73. @jessicamauerhan | Madison PHP | https://joind.in/16022 Dependency Injection 73 <?php //.. class UserExportCommand extends Command { //.. protected function execute(InputInterface $input, OutputInterface $output) { $users = $this->getUserRepository()->findAll(); /** Processing Logic */ foreach ($users as $user) { $emailMessage = $this->getMessageFactory()->generate($user); /** More Processing Logic Here */ } } }
  • 74. @jessicamauerhan | Madison PHP | https://joind.in/16022 Controller Using Service Location 74 <?php namespace DemoAppBundleController; use FOSRestBundleControllerFOSRestController; use FOSRestBundleControllerAnnotationsGet; class UsersController extends FOSRestController { /** * @Get("/users/") */ public function getUsersAction() { $data = $this->getDoctrine()->getRepository('modelsUser')->findAll(); $view = $this->view($data, 200) ->setTemplate("AppBundle:Basic:json.twig") ->setTemplateVar('users'); return $this->handleView($view); } }
  • 75. @jessicamauerhan | Madison PHP | https://joind.in/16022 services: appbundle.controllers.users_controller: class: DemoAppBundleControllerUsersController calls: - [ setUserRepository, ["@appbundle.repositories.user"]] Defining A Controller As A Service 75
  • 76. @jessicamauerhan | Madison PHP | https://joind.in/16022 Controller using Dependency Injection 76 /** * @Route(service="appbundle.controllers.users_controller") */ class UsersController extends FOSRestController { protected $userRepository; public function setUserRepository($userRepository) { $this->userRepository = $userRepository; } /** * @Get("/users/") */ public function getUsersAction() { $data = $this->userRepository->findAll(); $view = $this->view($data, 200) ->setTemplate("AppBundle:Basic:json.twig") ->setTemplateVar('users'); return $this->handleView($view); } }
  • 77. @jessicamauerhan | Madison PHP | https://joind.in/16022 Current Project Status It's stable! ● New Symfony based Admin was launched after about 9 months (thanks developers!) ● Had a few bugs, took about 3 more months to be stable ● Over past year, close to 0 regressions (Thanks Behat!) ● Very limited downtime (Thanks Amazon, Elastic Beanstalk, Aurora!) 77
  • 78. @jessicamauerhan | Madison PHP | https://joind.in/16022 Technical Goals Happy Developers! ● Maintainable Code: Check ● Quality Code: Check ● Documentation: Check ● Rapid Development: Oh Yeah ● Easy Deployment: So Easy! ● Zero Regressions: Close Enough! 78
  • 79. @jessicamauerhan | Madison PHP | https://joind.in/16022 Rebuilding Our Foundation How We Used Symfony To Rewrite Our Application @jessicamauerhan | Madison PHP | https://joind.in/16022
  • 80. @jessicamauerhan | Madison PHP | https://joind.in/16022 Resources Gitflow: https://github.com/nvie/gitflow PSR-2: http://www.php-fig.org/psr/psr-2/ PHPMD: http://phpmd.org/ Pre-commit hook: https://gist.github.com/jmauerhan/d18e7c232acb3986134d Doctrine: http://www.doctrine-project.org/ Doctrine Migrations Bundle: http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html Doctrine Data Fixtures Bundle: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html Behat: http://docs.behat.org/en/v2.5/ XDebug Wizard: http://xdebug.org/wizard.php Sonata Project: https://sonata-project.org/bundles/admin/2-3/doc/index.html FOS Rest Bundle: http://symfony.com/doc/master/bundles/FOSRestBundle/index.html Nelmio API Doc Bundle: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Resources/doc/index.md 80