IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

Introducció

Es caracteritza per estar dins de la programació orientada a objectes i es pot definir com a un patró de disseny.

Principis de SOLID

Single Responsibility

Una classe o mètode ha de tenir una sola responsabilitat.

  • A class should have one, and only one, reason to change.
  • L'objectiu es que només existeixi una sola responsabilitat
  • Els interessa canviar el codi el menys possible per poder reutilitzar-ho

NOTA: middleware comprova que estem loguejats

Single Responsibility Principle example with Laravel

Preparar l'entorn i crear directori on es vol treballar.

$ cd 
$ mkdir Code && cd Code 

Crear una nova aplicació Laravel

$ laravel new solid_laravel
Crafting application...
Generating optimized class loader
Compiling common classes
Application key [RvCcSkj40WvqGVLNchKDL68KlaK8jTk3] set successfully.
Application ready! Build something amazing.

Accedir al directori de l'aplicació i inicialitzar Git

$ cd solid_laravel
$ git init
Initialized empty Git repository in /home/pdavila/github/solid_laravel/.git/

Afegir l'origin del repositori

$ git remote add origin [email protected]:pdavila13/solid_laravel.git

Comprovar que tenim bé el repositori.

$ git remote -v
origin	[email protected]:pdavila13/solid_laravel.git (fetch)
origin	[email protected]:pdavila13/solid_laravel.git (push)

Pujar el codi net al repositori

$ git pull origin master
$ git status
$ git add .
$ git commit -a -m "First commit" 
$ git push origin master

Executar l'aplicació en mode local

$ php artisan serve
Laravel development server started on http://localhost:8000/
[Wed Apr  8 19:38:25 2015] 127.0.0.1:60427 [200]: /favicon.ico
[Wed Apr  8 19:38:25 2015] 127.0.0.1:60428 [200]: /favicon.ico

Comprovar l'estat de l'aplicació en Vangrant

$ vagrant global-status
id       name    provider   state   directory                                        
-------------------------------------------------------------------------------------
243fadf  default virtualbox running /home/pdavila/.composer/vendor/laravel/homestead 
 
The above shows information about all known Vagrant environments
on this machine. This data is cached and may not be completely
up-to-date. To interact with any of the machines, you can go to
that directory and run Vagrant, or you can use the ID directly
with Vagrant commands from any directory. For example:
"vagrant destroy 1a2b3c4d"

Accedir a la base de dades local i mostrar les base de dades existents.

$ sudo mysql -psecret

mysql> SHOW DATABASES;
+-------------------------+
| Database                |
+-------------------------+
| information_schema      |
| homestead               |
| laravelnotificationsapp |
| mysql                   |
| performance_schema      |
| solidlaravel            |
+-------------------------+
6 rows in set (0.00 sec)

mysql> exit
Bye

Canviar el namespace de l'aplicació utilitzant la comanda artisan.

$ php artisan app:name SolidLaravel
Application namespace set!

Afegir dades a la base de dades.

$ php artisan tinker
Psy Shell v0.4.4 (PHP 5.6.6-1+deb.sury.org~utopic+1 — cli) by Justin Hileman
>>> $invoice = new \SolidLaravel\Invoices();
=> <SolidLaravel\Invoices #00000000562a43b7000000006c585db7> {}
>>> $invoice->totalAmmount=4532;
=> 4532
>>> $invoice->save();
=> true
>>> exit
Exit:  Goodbye.
  • CODI DE L'EXERCICI:
  • InvoiceReport.php
<?php namespace SolidLaravel;

class InvoiceReport {


    private $id;

    private $invoice;

    function __construct($id)
    {

        if( ! \Auth::user()) {
            throw new \Exception("Authentication needed to obtain data");
        }

        $this->id = $id;
        $this->invoice = $this->getFromDatabase($id);
    }

    private function getFromDatabase($id)
    {
        return $this->invoice = Invoices::find($id);
    }

    public function show(){
        return "<strong>" . $this->invoice->totalAmmount . " </strong>";
    }
}
  • Invoice.php
<?php namespace SolidLaravel;

use Illuminate\Database\Eloquent\Model;

class Invoices extends Model {

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['totalAmmount'];

}
  • InvoiceController.php
<?php namespace SolidLaravel\Http\Controllers;

use SolidLaravel\Http\Requests;
use SolidLaravel\Http\Controllers\Controller;

use Illuminate\Http\Request;
use SolidLaravel\InvoiceReport;

class InvoiceController extends Controller {

    /**
     * Show invoice
     *
     * @return Response
     */
    public function index()
    {
        $invoice = new InvoiceReport(1);
        return view('invoice')->with('totalAmmount',$invoice->show());
    }

}
  • invoice.blade.php
@extends('app')

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-10 col-md-offset-1">
                <div class="panel panel-default">
                    <div class="panel-heading">Invoice</div>

                    <div class="panel-body">
                        Invoice ammount: {!! $totalAmmount or 'no data available!'  !!}
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection
  • routes.php
<?php

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/

Route::get('/', '[email protected]');

Route::get('home', '[email protected]');

Route::get('invoice', '[email protected]');

Route::controllers([
	'auth' => 'Auth\AuthController',
	'password' => 'Auth\PasswordController',
]);

SOLVED. Single Responsibility Principle example with Laravel

  • solid_laravel/app/Output/InvoiceShow.php
<?php
/**
 * Created by PhpStorm.
 * User: pdavila
 * Date: 15/04/15
 * Time: 21:33
 */

namespace SolidLaravel\Output;


class InvoiceShow {
    public function show($invoice) {

        return "<strong>" . $invoice->totalAmmount . "</strong>";

    }
}
  • solid_laravel/app/Repositories/InvoiceRepository.php
<?php
/**
 * Created by PhpStorm.
 * User: pdavila
 * Date: 15/04/15
 * Time: 21:34
 */

namespace SolidLaravel\Repositories;

use SolidLaravel\Invoices;

class InvoiceRepository {

    public function get($id) {

        return Invoices::find($id);

    }
}
  • solid_laravel/app/Http/Controllers/InvoiceController.php
<?php namespace SolidLaravel\Http\Controllers;


use SolidLaravel\Http\Requests;
use SolidLaravel\Http\Controllers\Controller;
use Illuminate\Http\Request;
use SolidLaravel\InvoiceReport;
use SolidLaravel\Output\InvoiceShow;
use SolidLaravel\Repositories\InvoiceRepository;

class InvoiceController extends Controller {

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct() {

        $this->middleware('auth');

    }

    /**
     * Show invoice
     *
     * @return Response
     */
    public function index() {

        $invoice = new InvoiceReport(new InvoiceRepository(),1);

        return view('invoice')->with('totalAmmount',$invoice->show(new InvoiceShow()));

    }

}
  • solid_laravel/app/InvoiceReport.php
<?php namespace SolidLaravel;

use SolidLaravel\Output\InvoiceShow;
use SolidLaravel\Repositories\InvoiceRepository;

class InvoiceReport {


    private $invoice;

    function __construct(InvoiceRepository $repo, $id) {

        $this->invoice = $repo->get($id);

    }

    public function show(InvoiceShow $i) {

        return $i->show($this->invoice);

    }
}

Pràctica inicial

Aquest és el codi que dona al principi amb el nom: SalesReporter.php

<?php namespace Acme\Reporting;

use Auth, DB, Execption;

class SalesReporter {
	public function between($startDate, $endDate) {
		// perform authentication
		if(!Auth::check()) throw new Exception('Authentication required for reporting.');

		// get sales form db
		$sales = $this->queryDBForSalesBetween($startdate, $endDate);

		// return results
		return $this->format($sales);
	}

	protected function queryDBForSalesBetween($startdate, $endDate) {
		return DB::table('sales')->whereBetween('created_at', [$startdate, $endDate])->sum('charge') / 100;
	}

	protected function format($sales) {
		return "<h1>Sales: $sales</h1>";
	}
}

?>

Ara anem a modificar aquest codi per que compleixi el primer principi

Open Closed Principle

Les entitats de software(classes, mètodes) si volen compler amb el open closed han de ser obertes a l'extensió augmentar la funcionalitat però tancades a la modificació de codi.

  • Entities should be open for extension, but closed for modification.
  • Change behavior witchout modifying source code.
  • Avoit code rot(pudrir-se).
  • Separate extensible behavior behind an interface, and flip the dependencies.
  • El codi ha de poder ser abstracte
  • Evitar que el nostre codi comence a podrir-se.
  • La base de no tocar codi (els programadors son fallibres - fallar al codi) es a dir fer que codi faci unes coses sense canviar-lo

Square.php

<?php namespace Acme;

	class Square {
		public $width;
		public $height;

		function __construct($width, $height) {
			$this->height = $height;
			$this->width = $width;
		}
	}  
?>

AreaCalculate.php

<?php namespace Acme;

	class AreaCalculator {
		public function calculate($squares) {
			$area = 0;

			foreach ($squares as $square) {
				# code...
				$area[] = $square->width * $square->height;
			}

			return array_sum($area);
		}

	}
?>

Liskov substitution principle

ISP: Interface segregation principle

DIP: Dependency inversion principle

  • No dependre dels detalls sinó de les abstraccions.
  • Tot això es fa per evitar que el codi estigui duplicat, espaguetti code

Exemples:

// DEPENDECY INJECTION
class JeepWrangler {
    public function __construct(Petrol $fuel) {
        $this->fuel = $fuel;
    }

    public function refuel($litres) {
        return $litres * $this->fuel->getPrice();
    }
}

class Petrol {
    public function getPrice() {
        return 130.7;
    }
}

class Gasolina {
    public function getPrice() {
        return 130.7;
    }
}

$petrol = new Petrol;
$car = new JeepWrangler($petrol);

$cost = $car->refuel(60);
interface Fuel {
    public function getPrice();
}

// DEPENDECY INJECTION
class JeepWrangler {
    public function __construct(Fuel $fuel) {
        $this->fuel = $fuel;
    }

    public function refuel($litres) {
        return $litres * $this->fuel->getPrice();
    }
}

class Petrol implements Fuel{
    public function getPrice() {
        return 130.7;
    }
}

class Gasolina implements Fuel {
    public function getPrice() {
        return 140.7;
    }
}

$gasolina = new Gasolina;
$car = new JeepWrangler($gasolina);

$cost = $car->refuel(60);

Vocabulari

  • Dependency Injection, DI: En català la podem traduir com a Injecció de dependències i es defineix com a un patró de disseny orientat a objecte, en el que es subministren objectes a un classe en lloc de ser la pròpia classe qui crea l'objecte.
  • Repository o persistencia: Lloc on guarden les coses.
  • Middleware:
  • Don’t Repeat Yourself , DRY:
  • Write Everything Twice o We Enjoy Typing, WET:
  • Reflection: Inspeccionar i manipular les classes i interfícies (mètodes i camps) en temps d'execució, sense conèixer (en temps de compilació) els tipus i/o nombres de les classes especifiques amb les que s'esta treballant.

Exercicis

$ laravel new SolidLaravel
$ sudo joe /etc/hosts
192.168.10.10       solidlaravel.app
$ homestead edit

Recursos:

$ php artisan make:auth
$ php artisan make:model Invoices --migration
$ php artisan make:controller InvoicesController

CODI TAULES I SEED

En el vagrant:

$ php artisan migrate
$ php artisan migrate:refresh --seed
$ php artisan make:controller InvoicesController

ERROS

BindingResolutionException

Les interfícies no es podem utilitzar directament només serveixen per crear altres classes.

sempre que es crea una classe s'ha de dir de quin tipus es.

Vegeu també

Enllaços externs