Sharing Laravel - ZendCon 2015 Edition

60187fe0ab07ea5a46572a3ab05f61dd?s=47 Matt Stauffer
October 21, 2015
990

Sharing Laravel - ZendCon 2015 Edition

60187fe0ab07ea5a46572a3ab05f61dd?s=128

Matt Stauffer

October 21, 2015
Tweet

Transcript

  1. Sharing Laravel Laravel’s Best Assets @stauffermatt Bringing To Any Project

  2. Sharing Laravel @stauffermatt Matt Stauffer @stauffermatt Partner, Technical Director Tighten

    Co. tighten.co
  3. Sharing Laravel @stauffermatt Gainesville I’m going to say y’all a

    lot ( )
  4. Sharing Laravel @stauffermatt Who’s my audience?

  5. Sharing Laravel @stauffermatt Most Laravel tutorials assume a blank slate.

    (or, that you’re developing in Laravel at all)
  6. Sharing Laravel @stauffermatt The goal: Learn how to bring the

    best assets of the Laravel world to non-Laravel projects.
  7. Sharing Laravel @stauffermatt WHAT IS LARAVEL DOING RIGHT?

  8. Sharing Laravel @stauffermatt What is Laravel doing right? Modern coding

    practices Collection of great community packages: Symfony, League, PHPDotEnv, etc. Rapid application development Collection of great unique (Illuminate) packages Easy interfaces—contracts and simple, interchangeable mail/queue/cache/session/file/etc. drivers Thriving and positive community Adoption of advanced patterns & practices
  9. Sharing Laravel @stauffermatt My side (or main) project is in

    CodeIgniter/ZF1/etc… what do I do? Frequent question
  10. Sharing Laravel @stauffermatt WHY DON’T WE JUST REWRITE THE WHOLE

    APP IN LARAVEL? TERRIBLE IDEA
  11. Sharing Laravel @stauffermatt response to legacy code: trash it. Sharing

    Laravel @stauffermatt
  12. Sharing Laravel @stauffermatt Nuking your old project is a Bad

    Idea. 1. It’s going to take longer than you think. 2. It’s going to take longer than you think. 3. There’s more domain knowledge in your old project than you can imagine. 4. Development on the production site will halt during development. 5. It’s going to take longer than you think.
  13. Sharing Laravel @stauffermatt The incremental approach

  14. Sharing Laravel @stauffermatt UNDERCOVER LARAVEL

  15. Sharing Laravel @stauffermatt UNDERCOVER LARAVEL • If you’re working in

    a legacy codebase… Sneak it in.
 Test here. DI there. Illuminate component there. • Prove the value before you’re asking anyone to pay for it. • On every task or project, look for one way to make your codebase a little more Laravel-y.
 • If you’re working on a non-legacy, but non-Laravel, codebase…
 use the components.
  16. Sharing Laravel @stauffermatt The 6 steps of bringing the “best

    of Laravel” to your non-Laravel projects
  17. Sharing Laravel @stauffermatt Write good code STEP 1

  18. Sharing Laravel @stauffermatt Crap code
 wrapped in Laravel
 is still

    crap code.
  19. Sharing Laravel @stauffermatt WRITE GOOD CODE OOP best practices SOLID

    Law of Demeter Loose coupling Not just wrapping procedural code in classes STEP 1
  20. Sharing Laravel @stauffermatt WRITE GOOD CODE Design patterns Identifying best-practice,

    commonly-used patterns; giving each a name; and suggesting situations in which each will (and won’t) prove useful STEP 1
  21. Sharing Laravel @stauffermatt WRITE GOOD CODE Design patterns Examples: •

    Adapter Pattern • Singleton Pattern • Observer Pattern • Many more… STEP 1
  22. Sharing Laravel @stauffermatt WRITE GOOD CODE Keep it Simple, Stupid

    Sandi Metz’ Rules for Developers https://robots.thoughtbot.com/sandi-metz-rules-for-developers 1. Classes can be no longer than one hundred lines of code. 2. Methods can be no longer than five lines of code. 3. Pass no more than four parameters into a method. Hash options are parameters. 4. Controllers can instantiate only one object. Therefore, views can only know about one instance variable and views should only send messages to that object (@object.collaborator.value is not allowed). STEP 1 “You should break these rules only if you have a good reason or your pair lets you.”
  23. Sharing Laravel @stauffermatt Learn from Laravel STEP 2

  24. Sharing Laravel @stauffermatt LEARN FROM LARAVEL Subscribe to Laracasts STEP

    2
  25. Sharing Laravel @stauffermatt Read the books From Apprentice to Artisan


    Taylor Otwell Implementing Laravel
 Chris Fidao Laravel Up & Running (2016)
 Matt Stauffer
 laravelupandrunning.com LEARN FROM LARAVEL STEP 2
  26. Sharing Laravel @stauffermatt IRC like it’s 1999 (or Slack like

    it’s 2015) Freenode #laravel / larachat.co You could hang out in #laravel and never have written a line of Laravel code in your life. LEARN FROM LARAVEL STEP 2
  27. Sharing Laravel @stauffermatt Read the source LEARN FROM LARAVEL STEP

    2
  28. Sharing Laravel @stauffermatt Modernize your foundation STEP 3

  29. Sharing Laravel @stauffermatt MODERNIZE YOUR FOUNDATION Code standards PSR-2 in

    Laravel 5! PHP CodeSniffer (PHPStorm, Phil Sturgeon blog post on Sublime Text) PHP-CS-Fixer (Automatically fixes your code) Nitpick.io (comments on your pull requests notifying about PSR-2 violations—tweet @adamwathan for beta access) STEP 3
  30. Sharing Laravel @stauffermatt Stay Classy Procedural code/functions -> methods on

    classes -> retroactive designed OOP Namespaces & autoloading STEP 3 MODERNIZE YOUR FOUNDATION
  31. Sharing Laravel @stauffermatt Autoloading & PSR-4 Autoload your classes PSR-4

    autoloading Drop as many includes/requires as possible STEP 3 MODERNIZE YOUR FOUNDATION
  32. Sharing Laravel @stauffermatt Bug-driven testing For every bug reported… Write

    a (failing) test. Then fix the bug by writing code that makes the test pass. Repeat. STEP 3 MODERNIZE YOUR FOUNDATION
  33. Sharing Laravel @stauffermatt Learn more STEP 3 https://speakerdeck.com/mattstauffer/
 why-modern-php-is-awesome-and-how-you-can-use-it-today MODERNIZE

    YOUR FOUNDATION
  34. Sharing Laravel @stauffermatt Compose all the things STEP 4

  35. Sharing Laravel @stauffermatt COMPOSE ALL THE THINGS What is
 Composer?

    Dependency manager for PHP (Like Ruby Gems, Node Package Manager, etc.) STEP 4
  36. Sharing Laravel @stauffermatt Community packages Packagist Symfony PHP League DIY

    COMPOSE ALL THE THINGS STEP 4
  37. Sharing Laravel @stauffermatt Laravel-as-a- component STEP 5

  38. Sharing Laravel @stauffermatt LARAVEL-AS-A-COMPONENT Just a little bit of Laravel

    Laravel-as-a-frontend: Build a modern Laravel app that consumes data from your existing site Laravel-as-a-backend: Build a modern Laravel app that replaces an existing data store STEP 5
  39. Sharing Laravel @stauffermatt CraftCMS CraftCMS is based on Yii REST

    API (via Plugin) Eloquent database models for reading LARAVEL-AS-A-COMPONENT STEP 5
  40. Sharing Laravel @stauffermatt “The Right Toolbox” Anthony Colangelo at HappyCog

    Eloquent objects to describe your Craft database structure https://speakerdeck.com/ acolangelo/the-right-toolbox LARAVEL-AS-A-COMPONENT STEP 5
  41. Sharing Laravel @stauffermatt https://speakerdeck.com/acolangelo/the-right-toolbox

  42. Sharing Laravel @stauffermatt DEEP/OpenAPI/Export It Same thing, but for ExpressionEngine

    https://github.com/rsanchez/Deep https://github.com/putyourlightson/open-api https://www.mithra62.com/ (Export It) LARAVEL-AS-A-COMPONENT STEP 5
  43. Sharing Laravel @stauffermatt Illuminate your project STEP 6

  44. Sharing Laravel @stauffermatt ILLUMINATE YOUR PROJECT Illuminate packages Mail Auth

    Queue Config Cache Session Workbench Validation Translation Support Routing Remote Pagination Log Http Html Hashing Events Database Cookie STEP 6 Commonly exported: • Database (Query Builder, Migrations & Seeding, Eloquent) • Queues • Container (IOC/DI container) • Cache • Session • Mail • Support (Collections, array helpers, etc.) • Many more…
  45. Sharing Laravel @stauffermatt TITLE FOR STEP 1 github.com/ mattstauffer/ Torch

    ILLUMINATE YOUR PROJECT STEP 6
  46. Sharing Laravel @stauffermatt /* Composer.json: { "require": { "illuminate/support": "~5.1.0"

    } } */ require_once('vendor/autoload.php'); // Collection $people = new Illuminate\Support\Collection([ 'Declan', 'Abner', 'Mitzi' ]); $people->each(function ($person) { echo "Well, howdy $person!<br>"; }); Simple Use Example
  47. Sharing Laravel @stauffermatt include_once('vendor/autoload.php'); $app = new Illuminate\Container\Container; $app->bind('app', $app);

    $app->bind('ClassName', function () { return new ClassName; }); $app->singleton('ClassName', function () { return new ClassName; }); $app->share(function () { return new ClassName }); $app->extend('ClassName', function ($app, $class) { $class->setThing('a', 'b'); }); $app->instance('ClassName', $instanceOfClassName); $app->alias('OriginalBinding', 'alias'); $app->make('ClassName'); $app->when('OneThing')->needs('StuffInterface')->give('WhiteStuff'); $app->when('OtherThing')->needs('StuffInterface')->give('BlackStuff'); Using the Container
  48. Sharing Laravel @stauffermatt $app = new Illuminate\Container\Container; $app->bind('app', $app); //

    Asks for an instance of ClassName; "auto-wiring" // means it resolves this class, even if it's never // been explicitly bound $object = $app->make('ClassName'); Container: make
  49. Sharing Laravel @stauffermatt $app = new Illuminate\Container\Container; $app->bind('app', $app); //

    Bind the ClassName string to run the Closure provided; // every time it's injected, will run Closure again $app->bind('ClassName', function () { return new ClassName; }); $object1 = $app->make('ClassName'); $object2 = $app->make('ClassName'); $object1 !== $object2 Container: bind
  50. Sharing Laravel @stauffermatt $app = new Illuminate\Container\Container; $app->bind('app', $app); //

    Bind the ClassName string to run the Closure provided; // caches the result and returns it every time in the future $app->singleton('ClassName', function () { return new ClassName; }); $object1 = $app->make('ClassName'); $object2 = $app->make('ClassName'); $object1 === $object2 Container: singleton
  51. Sharing Laravel @stauffermatt $app = new Illuminate\Container\Container; $app->bind('app', $app); $app['ClassName']

    = $app->share(function () { return new ClassName; }); $app->bindShared('ClassName', function () { return new ClassName; }); $app->singleton('ClassName', function () { return new ClassName; }); $classNameInstance = new ClassName; $app->instance('ClassName', $classNameInstance); Container: Singleton alternatives
  52. Sharing Laravel @stauffermatt $app = new Illuminate\Container\Container; $app->bind('app', $app); //

    Teaches the Container that, every time it resolves // this class (after this binding), it should also // perform the following behavior $app->extend('ClassName', function ($app, $class) { $class->setThing('a', 'b'); }); Container: extend
  53. Sharing Laravel @stauffermatt $object = $app->make('ClassName'); // Magic: class ClassName

    { public function __construct(OtherClass $other, ThirdClass $third) { $this->other = $other; $this->third = $third; } } // But what about... class OtherClass { public function __construct(ThingInterface $thing) { $this->thing = $thing; } } Container: Automatic dependency injection
  54. Sharing Laravel @stauffermatt $app = new Illuminate\Container\Container; $app->bind('app', $app); //

    Adds an alias that returns the same output // as an existing Binding $app->alias('OriginalBinding', 'alias'); $app->alias( 'MyConcreteClass', 'MyInterface', ); // Real-life examples: $app->alias( 'files', 'Illuminate\Filesystem\Filesystem', ); Container: alias
  55. Sharing Laravel @stauffermatt $app = new Illuminate\Container\Container; $app->bind('app', $app); //

    Conditionally binds injected dependencies depending on the // requesting class/interface $app->when('OneThing') ->needs('StuffInterface') ->give('ThisStuff'); $app->when('OtherThing') ->needs('StuffInterface') ->give('ThatStuff'); Container: when/ needs/give
  56. Sharing Laravel @stauffermatt $messageBag = new Illuminate\Support\MessageBag; // Do stuff

    $messageBag->add('error', 'Something happened!'); // Do other stuff if ($messageBag->has('error')): ?> <h2>Errors</h2> <ul> <?php foreach ($messageBag->get('error', 'ERROR! :message') as $message): ?> <li><?= $message; ?></li> <?php endforeach; ?> </ul> <?php endif; ?> Support: Message
 Bag
  57. Sharing Laravel @stauffermatt // Collections $names = new Collection(['Declan', 'Abner',

    'Mitzi']); $names->filter(function ($name) { // Filter out any names which start with "D" return substr($name, 0, 1) !== 'D'; })->map(function ($name) { // Decorate each name return "<i>{$name}</i>"; })->each(function ($name) { // Output each name echo "Collection name: $name\n"; }); // More at http://laravel.com/docs/5.1/collections Support: Collections
  58. Sharing Laravel @stauffermatt $personRecord = [ 'first_name' => 'Mohammad', 'last_name'

    => 'Gufran' ]; $record = new Fluent($personRecord); $record->address('hometown, street, house'); echo $record->first_name . "\n"; echo $record->address . "\n"; // Fluent is like the associative array sibling of Collections // class Fluent implements ArrayAccess, Arrayable, Jsonable, JsonSerializable echo json_encode($record); echo $record->toArray(); Support: Fluent
  59. Sharing Laravel @stauffermatt // Pluralizer use Illuminate\Support\Pluralizer; $item = 'goose';

    echo "One {$item}, two " . Pluralizer::plural($item); $item = 'moose'; echo "One {$item}, two " . Pluralizer::plural($item); // Str use Illuminate\Support\Str; if (Str::contains('My fourteenth visit', 'first')) { echo 'Howdy!'; } else { echo 'Nice to see you again.'; } Support: Str & Pluralizer
  60. Sharing Laravel @stauffermatt $capsule = new Illuminate\Database\Capsule\Manager; $capsule->addConnection([ 'driver' =>

    'mysql', 'host' => 'localhost', 'database' => 'database', 'username' => 'root', 'password' => 'supersecret!', 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '' ]); $capsule->bootEloquent(); // Eloquent model definition class User extends Illuminate\Database\Eloquent\Model {} // Eloquent model usage $user = User::find(1); Database & Eloquent
  61. Sharing Laravel @stauffermatt $queue = new Queue($container); // ... add

    Config, Request, and Encryption dependencies... // Uses 'sync' driver if no connection set $queue->addConnection([ 'driver' => 'iron', 'project' => 'your-project-id', 'token' => 'your-token', 'queue' => 'illuminate-test', 'encrypt' => true, ]); Queue::push('doThing', ['string' => 'sync-' . date('r')]); class doThing { public function fire($job, $data) { // Do stuff... $job->delete(); } } Queue
  62. Sharing Laravel @stauffermatt // Prep $queue like in previous example...

    $worker = new Illuminate\Queue\Worker($queue->getQueueManager()); // Params list for 'pop': // * Name of the connection to use // * Name of the queue to use // * Number of seconds to delay a job if it fails // * Maximum amount of memory to use // * Time (in seconds) to sleep when no job is returned // * Maximum number of times to retry the given job // before discarding it while (true) { try { $worker->pop('default', 'worker-test', 3, 64, 30, 3); } catch (Exception $e) { // Handle job exception } } Queue: Worker
  63. Sharing Laravel @stauffermatt // Prepare dependencies $container = new Container;

    $container['files'] = new Filesystem; $container['config'] = [ 'cache.default' => 'file', 'cache.stores.file' => [ 'driver' => 'file', 'path' => __DIR__ . '/cache' ] ]; // Create cache manager $cacheManager = new CacheManager($container); // Get specific cache instance (no parameter means "default") $cache = $cacheManager->store(); $cache->put('test', 'This is loaded from cache.', 500); echo $cache->get('test'); Cache
  64. Sharing Laravel @stauffermatt $container = new Container; $container->bind('app', $container); $container['config']

    = new Config; $container['files'] = new Filesystem; $container['config'] = [ 'session' => [ 'lottery' => [2, 100], 'cookie' => 'laravel_session', 'path' => '/', 'domain' => null, 'driver' => 'file', 'files' = __DIR__ . '/sessions' ] ]; $cookieName = $container['session']->getName(); if (isset($_COOKIE[$cookieName])) { if ($sessionId = $_COOKIE[$cookieName]) { $container['session']->setId($sessionId); } } // Boot the session $container['session']->start(); Session: Prepare
  65. Sharing Laravel @stauffermatt $container['session']->put('test', 'abcdef'); // Save the session $container['session']->save();

    // Store the session ID in our cookie $cookie = new Symfony\Component\HttpFoundation\Cookie( $container['session']->getName(), $container['session']->getId(), time() + ($container['config']['session.lifetime'] * 60), '/', null, false ); setcookie( $cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain() ); Session: Set
  66. Sharing Laravel @stauffermatt if ($container['session']->has('test')) { echo 'Set<br> Value: '

    . $container['session']->get('test'); } else { echo 'Not set'; } Session: Get
  67. Sharing Laravel @stauffermatt // Above: prepare writer, logger // Choose

    a transport (SMTP, PHP Mail, Sendmail, Mailgun, Mandrill, Log) $transport = Swift_SmtpTransport::newInstance(getenv('SMTP_HOST'), getenv('SMTP_PORT')); // $transport = Swift_MailTransport::newInstance(); // $transport = Swift_SendmailTransport::newInstance('/usr/sbin/sendmail -bs'); // $transport = new MailgunTransport(getenv('MAILGUN_SECRET'), getenv('MAILGUN_DOMAIN')); // $transport = new MandrillTransport(getenv('MANDRILL_SECRET')); // $transport = new SesTransport(new SesClient($sesConfig)); // $transport = new LogTransport($logger->getMonolog()); // SMTP specific configuration $transport->setUsername(getenv('SMTP_USERNAME')); $transport->setPassword(getenv('SMTP_PASSWORD')); $transport->setEncryption(true); $swift = new Swift_Mailer($transport); $finder = new FileViewFinder(new Filesystem, ['views']); $resolver = new EngineResolver; // Determine which template engine to use $resolver->register('php', function () { return new PhpEngine; }); $view = new Factory($resolver, $finder, new Dispatcher); $mailer = new Mailer($view, $swift); // Optionally set logger, queue manager, and container Mail: prepare
  68. Sharing Laravel @stauffermatt $data = [ 'greeting' => 'Nice work.',

    ]; $mailer->send('email.welcome', $data, function ($message) { $message->from(getenv('MAIL_FROM_ADDRESS'), 'Code Guy'); $message->to(getenv('MAIL_TO_ADDRESS'), 'Barack Obama'); $message->bcc('matt@tighten.co'); $message->attach('secret_documents.zip'); $message->subject('Yo!'); }); Mail: send
  69. Sharing Laravel @stauffermatt $app = new Illuminate\Container\Container; $app->bind('app', $app); $providers

    = [ 'Illuminate\Queues\QueueServiceProvider', 'Illuminate\Events\EventServiceProvider' ]; foreach ($providers as $provider) { with (new $provider($app))->register(); } $aliases = [ 'Queue' => 'Illuminate\Support\Facades\Queue', 'Config' => 'Illuminate\Support\Facades\Config', ]; foreach ($aliases as $alias => $class) { class_alias($class, $alias); } Illuminate\Support\Facades\Facade::setFacadeApplication($app); foreach ($providers as $provider) { with (new $provider($app))->boot(); } Booting: Service Providers, Aliases, and Façades
  70. Sharing Laravel @stauffermatt ILLUMINATE YOUR CODEBASE Illuminate Contracts Interfaces that

    define how all of the Illuminate components interface—and allow for easy third-party replacements STEP 6
  71. Sharing Laravel @stauffermatt <?php namespace Illuminate\Contracts\Cache; interface Store { public

    function get($key); public function put($key, $value, $minutes); public function increment($key, $value = 1); public function decrement($key, $value = 1); public function forever($key, $value); public function forget($key); public function flush(); public function getPrefix(); } Example Contract: Cache
  72. Sharing Laravel @stauffermatt <?php namespace Illuminate\Cache; ... use Illuminate\Contracts\Cache\Store; ...

    class CacheManager implements FactoryContract { ... public function repository(Store $store) { $repository = new Repository($store); if ($this->app->bound('Illuminate\Contracts\Events\Dispatcher')) { $repository->setEventDispatcher( $this->app['Illuminate\Contracts\Events\Dispatcher'] ); } return $repository; Example Contract Usage: Cache Manager
  73. Sharing Laravel @stauffermatt Conclusion

  74. Sharing Laravel @stauffermatt Know why you’re here What is it

    about Laravel that brought you to this talk?
  75. Sharing Laravel @stauffermatt Bring that, incrementally, to every app •

    OOP & Design/Architecture Patterns • Modernize codebase • Composer & Community Packages • Undercover Laravel • Laravel as a Component • Illuminate Components
  76. Sharing Laravel @stauffermatt Additional resources for legacy codebases Modernizing Legacy

    Applications in PHP 
 Paul Jones Working Effectively with Legacy Code
 Michael Feathers Refactoring 
 Martin Fowler, Kent Beck, et al. Mastering Object Oriented PHP
 Brandon Savage
  77. Sharing Laravel @stauffermatt Compelling action slide Review and slides: https://joind.in/15549