Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Микрофреймворк Silex. Берем в дорогу только не...

fwdays
October 18, 2013

Микрофреймворк Silex. Берем в дорогу только необходимое

fwdays

October 18, 2013
Tweet

More Decks by fwdays

Other Decks in Programming

Transcript

  1. Background Experience  7 Years with PHP  5 Years

    with Zend Framework  3 Years with Magento  3 Years as Team Lead Interests  Virtual environment. (Vagrant, LXC Containers, Chef)  Continuous Integration. (Jenkins, Behat, Functional testing)  Agile. (Code review, Tests, Pair programming)  Building Software. (Phing, Composer)
  2. Insights  We produce less new code for sale 

    Mostly integration issues  Less Code – Less Bugs
  3. Frameworks and Libraries “A library is essentially a set of

    functions that you can call, these days usually organized into classes.” “A framework embodies some abstract design, with more behavior built in. In order to use it you need to insert your behavior into various places in the framework either by subclassing or by plugging in your own classes. The framework's code then calls your code at these points.” Martin Fowler http://martinfowler.com/bliki/InversionOfContr ol.html
  4. Frameworks by Igor Wiedler Conventions  Codding standard  Directory

    layout  Community Libraries  Solved problems  Reusable code Wiring  Base Config  Library glue  Bootstrap
  5. Micro Frameworks Conventions  Codding standard  Directory layout 

    Community Libraries  Solved problems  Reusable code Wiring  Base Config  Library glue  Bootstrap
  6. Micro frameworks “Use Silex if you are comfortable with making

    all of your own architecture decisions and full stack Symfony2 if not.” Dustin Whittle
  7. Silex is largest micro framework in the world  16831

    NCLOC in 280 classes  4018 NCLOC for “Hello World” application Dependencies  pimple/pimple  psr/log  symfony/debug  symfony/event-dispatcher  symfony/http-kernel  symfony/http-foundation  symfony/routing
  8. Pimple. Lazy loading. <?php require_once 'vendor/autoload.php'; class Service { public

    function __construct() { echo __CLASS__; } } $c = new \Pimple(); $c['service'] = function () { return new Service(); }; $service = $c['service'];
  9. Pimple. Dependency Injection. <?php $container['session_storage_class'] = 'SessionStorage'; $container['session_storage'] = function

    ($c) { return new $c['session_storage_class']($c['cookie_name']); }; $container['session_storage']();
  10. Pimple. Singletons <?php $app['service'] = $app->share(function () { return new

    Service(); }); // Pimple. Share services. public static function share($callable) { return function ($c) use ($callable) { static $object; if (null === $object) { $object = $callable($c) } return $object; } }
  11. Restful application with Silex Requirements:  QA can create/view/edit/delete Project

     QA can create/view/edit/delete User Stories for Project  QA can create/view/edit/delete Test Cases for User Story
  12. Init Application # Init Repository $ git init # Add

    Some documentation $ echo Reqman 'Application' > README.md $ composer init # Answer questions and define Reqman application # Require Silex micro framework $ composer require silex/silex # Init Endpoint $ mkdir web && cd touch api.php
  13. Endpoint <?php // web/api.php require_once __DIR__.'/../vendor/autoload.php'; $app = new \Silex\Application();

    $app['debug'] = true; // Turn on errors $app->get('/', function() use($app) { return "Home Page"; }); $app->run();
  14. Restful Routes <?php // web/api.php ... $app->get("/project", function() use ($app)

    { return $app->json( array( array(‘name’ => ‘Project1’) ) ); }); $app->run(); // /api.php/project/ => [{‘name’ => ‘Project1’}]
  15. Restful Routes $app->get(‘/project’, function () { ... }); // View

    all $app->get(‘/project/{id}’, function () { ... }); // View one $app->post(‘/project’, function (Request $request) { ... }); // Create one $app->put(‘/project/{id}’, function (Request $request) { ... }); // Update one $app->delete(‘/project/{id}’, function (Request $request) { ... }); // Delete one
  16. Database $app->register(new \Silex\Provider\DoctrineServiceProvider(), array( 'db.options' => array( 'driver' => 'pdo_mysql',

    'dbname' => 'reqman', 'host' => 'localhost', 'user' => 'root', 'password' => '' ), )); /** @var \Doctrine\DBAL\Connection $dbAdapter */ $dbAdapter = $app['db'];
  17. Provider Interface Class MyServiceProvider implements ServiceProviderInterface { public function register(Application

    $app) { $app['service'] = $app->share(function () use ($app) { $service = new Service(); $service->setName($app[’name']); return $service }); } }
  18. Organizing Controllers $app->mount( '/project', new \Reqman\ProjectBundle\Controller\ProjectController() ); // Interface for

    custom controllers interface ControllerProviderInterface { public function connect(Application $app); }
  19. Project Controller <?php class ProjectController implements ControllerProviderInterface { public function

    connect(Application $app) { $controller = $app['controllers_factory']; $controller->get("/", function() use ($app) { $dbAdapter = $app['db']; $sql = "SELECT * FROM `project`"; $result = $dbAdapter->fetchAll($sql); return $app->json($result); }); } }
  20. Project Controller <?php class ProjectController implements ControllerProviderInterface { public function

    connect(Application $app) { $controller = $app['controllers_factory']; $controller->post("/", function(Request $request) use ($app) { $dbAdapter = $app['db']; $params = $request->request->all(); $dbAdapter->insert('project', $params); return $app->json("", 201); }); } }
  21. Testing # Add testing framework $ composer require --dev behat/behat

    # Unfortunately we need much more dependencies "require-dev":{ "symfony/browser-kit" : "2.1.*", "behat/behat": "*", "behat/mink": "*", "behat/mink-extension": "*", "behat/mink-goutte-driver": "*", "behat/mink-selenium2-driver": "*", "behat/mink-browserkit-driver": "*", "behat/common-contexts": "*", "kriswallsmith/buzz": "*", "phpunit/phpunit": "*", "phpunit/dbunit": "*" }
  22. Migrations # Add Migration library $ composer require --dev knplabs/migration-service-provider

    $ composer require --dev knplabs/console-service-provider class ProjectMigration extends AbstractMigration { public function schemaUp(Schema $schema) { $projectTable = $schema->createTable('project'); $projectTable->addColumn('id', 'integer', array( 'unsigned' => true, 'autoincrement' => true )); $projectTable->addColumn('name', 'string'); $projectTable->setPrimaryKey(array('id')); } }
  23. Behat for API testing # Add extra contexts to test

    Restful Api $ composer require --dev behat/common-contexts Feature: Project As a QA I should be able to manage projects Scenario: List of projects When I send a GET request to "/project/" Then the response code should be 200 And response should contain json: """ [ {"id": “1", "name": "project 1”}, {"id": “2", "name": "project 2”} ] """
  24. Behat and fixtures # Add own context in behat.yml default:

    context: class: FeatureContext <?php // Customize context to load data fixtures right before tests class FeatureContext extends BehatContext { /** @BeforeScenario */ public function before($event) { //load data into database } /** @AfterScenario */ public function after($event) { // clean data } ?>
  25. Fixtures # PHPUnit_Extensions_Database # PHPUnit_Extensions_Database_DefaultTester $data = new \PHPUnit_Extensions_Database_DataSet_YamlDataSet( dirname(__FILE__)

    . "fixtures/data.yml"); // fixtures/data.yml project: - id: 1 name: project 1 - id: 2 name: project 2 # Bug with foreign keys in innodb. Solution is to rewrite: # PHPUnit_Extensions_Database_Operation_Truncate
  26. Continuous Integration with Travis $ touch .travis.yml $ cat .travis.xml

    language: php php: - 5.3 env: - DB=mysql before_script: # Install apache and configure host ... # Install packages - composer self-update && composer install –dev # Apply migrations - mysql -e 'create database test_reqman;' - php console.php knp:migration:migrate script: - ./bin/behat