Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

Sharing Laravel @stauffermatt Matt Stauffer @stauffermatt Partner, Technical Director Tighten Co. tighten.co

Slide 3

Slide 3 text

Sharing Laravel @stauffermatt Gainesville I’m going to say y’all a lot ( )

Slide 4

Slide 4 text

Sharing Laravel @stauffermatt Who’s my audience?

Slide 5

Slide 5 text

Sharing Laravel @stauffermatt Most Laravel tutorials assume a blank slate. (or, that you’re developing in Laravel at all)

Slide 6

Slide 6 text

Sharing Laravel @stauffermatt The goal: Learn how to bring the best assets of the Laravel world to non-Laravel projects.

Slide 7

Slide 7 text

Sharing Laravel @stauffermatt WHAT IS LARAVEL DOING RIGHT?

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Sharing Laravel @stauffermatt My side (or main) project is in CodeIgniter/ZF1/etc… what do I do? Frequent question

Slide 10

Slide 10 text

Sharing Laravel @stauffermatt WHY DON’T WE JUST REWRITE THE WHOLE APP IN LARAVEL? TERRIBLE IDEA

Slide 11

Slide 11 text

Sharing Laravel @stauffermatt response to legacy code: trash it. Sharing Laravel @stauffermatt

Slide 12

Slide 12 text

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.

Slide 13

Slide 13 text

Sharing Laravel @stauffermatt The incremental approach

Slide 14

Slide 14 text

Sharing Laravel @stauffermatt UNDERCOVER LARAVEL

Slide 15

Slide 15 text

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.

Slide 16

Slide 16 text

Sharing Laravel @stauffermatt The 6 steps of bringing the “best of Laravel” to your non-Laravel projects

Slide 17

Slide 17 text

Sharing Laravel @stauffermatt Write good code STEP 1

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

Sharing Laravel @stauffermatt WRITE GOOD CODE OOP best practices SOLID Law of Demeter Loose coupling Not just wrapping procedural code in classes STEP 1

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Sharing Laravel @stauffermatt WRITE GOOD CODE Design patterns Examples: • Adapter Pattern • Singleton Pattern • Observer Pattern • Many more… STEP 1

Slide 22

Slide 22 text

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.”

Slide 23

Slide 23 text

Sharing Laravel @stauffermatt Learn from Laravel STEP 2

Slide 24

Slide 24 text

Sharing Laravel @stauffermatt LEARN FROM LARAVEL Subscribe to Laracasts STEP 2

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

Sharing Laravel @stauffermatt Read the source LEARN FROM LARAVEL STEP 2

Slide 28

Slide 28 text

Sharing Laravel @stauffermatt Modernize your foundation STEP 3

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Sharing Laravel @stauffermatt Stay Classy Procedural code/functions -> methods on classes -> retroactive designed OOP Namespaces & autoloading STEP 3 MODERNIZE YOUR FOUNDATION

Slide 31

Slide 31 text

Sharing Laravel @stauffermatt Autoloading & PSR-4 Autoload your classes PSR-4 autoloading Drop as many includes/requires as possible STEP 3 MODERNIZE YOUR FOUNDATION

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Sharing Laravel @stauffermatt Compose all the things STEP 4

Slide 35

Slide 35 text

Sharing Laravel @stauffermatt COMPOSE ALL THE THINGS What is
 Composer? Dependency manager for PHP (Like Ruby Gems, Node Package Manager, etc.) STEP 4

Slide 36

Slide 36 text

Sharing Laravel @stauffermatt Community packages Packagist Symfony PHP League DIY COMPOSE ALL THE THINGS STEP 4

Slide 37

Slide 37 text

Sharing Laravel @stauffermatt Laravel-as-a- component STEP 5

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

Sharing Laravel @stauffermatt CraftCMS CraftCMS is based on Yii REST API (via Plugin) Eloquent database models for reading LARAVEL-AS-A-COMPONENT STEP 5

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Sharing Laravel @stauffermatt https://speakerdeck.com/acolangelo/the-right-toolbox

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

Sharing Laravel @stauffermatt Illuminate your project STEP 6

Slide 44

Slide 44 text

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…

Slide 45

Slide 45 text

Sharing Laravel @stauffermatt TITLE FOR STEP 1 github.com/ mattstauffer/ Torch ILLUMINATE YOUR PROJECT STEP 6

Slide 46

Slide 46 text

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!
"; }); Simple Use Example

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

Sharing Laravel @stauffermatt $messageBag = new Illuminate\Support\MessageBag; // Do stuff $messageBag->add('error', 'Something happened!'); // Do other stuff if ($messageBag->has('error')): ?>

Errors

    get('error', 'ERROR! :message') as $message): ?>
  • = $message; ?>
Support: Message
 Bag

Slide 57

Slide 57 text

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 "{$name}"; })->each(function ($name) { // Output each name echo "Collection name: $name\n"; }); // More at http://laravel.com/docs/5.1/collections Support: Collections

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

Sharing Laravel @stauffermatt if ($container['session']->has('test')) { echo 'Set
Value: ' . $container['session']->get('test'); } else { echo 'Not set'; } Session: Get

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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('[email protected]'); $message->attach('secret_documents.zip'); $message->subject('Yo!'); }); Mail: send

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

Sharing Laravel @stauffermatt

Slide 72

Slide 72 text

Sharing Laravel @stauffermatt app->bound('Illuminate\Contracts\Events\Dispatcher')) { $repository->setEventDispatcher( $this->app['Illuminate\Contracts\Events\Dispatcher'] ); } return $repository; Example Contract Usage: Cache Manager

Slide 73

Slide 73 text

Sharing Laravel @stauffermatt Conclusion

Slide 74

Slide 74 text

Sharing Laravel @stauffermatt Know why you’re here What is it about Laravel that brought you to this talk?

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

Sharing Laravel @stauffermatt Compelling action slide Review and slides: https://joind.in/15549