[php] [laravel] Interfaces en Factories in Laravel 5

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • TheNephilim
  • Registratie: September 2005
  • Laatst online: 09-09 12:00
Na een tijdje Googelen toch hier maar eens een topic plaatsen. Er zijn weinig concrete voorbeelden te vinden over hoe ik het onderstaande in Laravel 5 moet implementeren. Op basis van dit topic ben ik al eind, maar hoe nu in Laravel?

Calculator

Het idee is dat ik meerdere 'calculators' heb, dus bijv. een 'PrintPosterPriceCalculator' en een 'PrintLabelPriceCalculator'. Daarnaast moet er per jaar een verschillende implementatie kunnen zijn van die 'calculator'. Dus een 'PrintPosterPriceCalculator2015' en een 'PrintPosterPriceCalculator2016' bijvoorbeeld.

Laravel

Maar hoe doen we dit nu netjes in Laravel? Er zijn ServiceProviders in Laravel, maar ik kan geen voorbeeld vinden waarmee ik wat kan. Toch kom ik steeds terug op die ServiceProviders trouwens. Of moet ik gewoon factories en interfaces 'los' gebruiken om dit te regelen en heeft Laravel geen hulpmiddel hiervoor?

Gebruik

Een array met argumenten als input en een prijs als output, meer wil ik eigenlijk niet.

PHP:
1
2
3
4
5
// Zoiets bijv.
Calculator::result('printpostprice', ['year' => 2015, 'quantity' => 50]);

// Of toch 
PrintPosterPrice::result(['year' => 2015, 'quantity' => 50]);


Misschien kan iemand hier een schop in de goede richting geven? :+

Acties:
  • 0 Henk 'm!

  • sky-
  • Registratie: November 2005
  • Niet online

sky-

qn ella 👌

Klinkt als Strategy Pattern? Zie, bijv: https://sourcemaking.com/design_patterns/strategy/php

don't be afraid of machines, be afraid of the people who build and train them.


Acties:
  • 0 Henk 'm!

  • Barryvdh
  • Registratie: Juni 2003
  • Nu online
Je kan een Interface maken voor je PrintPosterPrice en dan in je ServiceProvider de specifiek implementatie binden aan die interface? Of bedoel je dat niet?

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface PrintPosterPriceInterface
{
    public function result(array $data);
}

class PrintPosterPrice2016 implements  PrintPosterPriceInterface
{
    public function result(array $data)
    {
        return 123;
    }
}

app()->bind('PrintPosterPriceInterface', function(){
    return new PrintPosterPrice2016;
});


Dan kan je hem laten resolven in een controller (typehinten op de Interface), of je kan hem gewoon vanuit de App container opvragen. (Of natuurlijk een Facade om dat met je static interface te doen).

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function getResult(PrintPosterPriceInterface $calc) {
    $price = $calc->result($data);
}

$price = app('PrintPosterPriceInterface')->result($data);

class PrintPostPrice extends Illuminate\Support\Facades\Facade
{
    protected static function getFacadeAccessor()
    {
        return 'PrintPosterPriceInterface';
    }
}
PrintPostPrice::result($data);


Dan kan je dus de implementatie op 1 plek in je app vervangen, zolang je je aan de interface houdt (waarbij een array waarschijnlijk niet heel betrouwbaar is, als je later kan veranderen zomaar).

Voor verschillende types zal je wel meerdere interfaces moeten maken lijkt me, want je kan niet zomaar een PrintPoster door PrintLabel calculator vervangen toch? Of hoe bedoelde je dat?

Edit: Ow je wil 1 class hebben die al die calculators aanroept? In dat geval zou je gewoon natuurlijk gewoon een class kunnen maken die afhankelijk van de 1ste param, een bepaalde interface/class resolved. Maar weet niet of je niet makkelijker gewoon verschillende interfaces kan maken, dan 1 hele algemene waar je een array in knalt?

[ Voor 10% gewijzigd door Barryvdh op 29-06-2015 16:09 ]


Acties:
  • 0 Henk 'm!

  • TheNephilim
  • Registratie: September 2005
  • Laatst online: 09-09 12:00
Voor wat jij beschrijft gebruik ik nu een Factory, het gaat me vooral om de specifieke Laravel oplossing.
Barryvdh schreef op maandag 29 juni 2015 @ 16:04:
Je kan een Interface maken voor je PrintPosterPrice en dan in je ServiceProvider de specifiek implementatie binden aan die interface? Of bedoel je dat niet?

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
interface PrintPosterPriceInterface
{
    public function result(array $data);
}

class PrintPosterPrice2016 implements  PrintPosterPriceInterface
{
    public function result(array $data)
    {
        return 123;
    }
}
De bovengenoemde interface en implementatie heb ik nu inderdaad, bijna letterlijk. Die Facade moet er inderdaad ook komen, PrintPostPrice bijvoorbeeld zoals je laat zien. Alleen wil ik aan de hand van (bijv.) een select veld een jaar laten kiezen. Zodat je kan kiezen voor welk jaar ik de 'PrintPosterPrice' wil laten bereken. Het idee is dat je per jaar de prijzen kan laten aanpassen en deze makkelijk kunt wisselen, maar ik wil het wel zo hebben dat ik ook nog prijzen van voorgaande jaren kan berekenen.

PHP:
1
2
3
app()->bind('PrintPosterPriceInterface', function(){
    return new PrintPosterPrice2016;
});


Bij bovenstaande zit ik vast aan het in de serviceprovider gedefineerde jaar. Nu heb ik iets als...

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// app/Calculators/PrintPoster/PrintPosterFactory.php

namespace App\Calculators;

use App\Calculators\PrintPoster\PrintPoster2015;

class PrintPosterFactory
{
    public static function make($year)
    {
        switch ($year) {

            case 2015:
                return new PrintPoster2015();

            default:
                throw new \Exception('Unknown implementation!');
        }
    }
}


Een factory per 'jaar' en 'type' calculator. Het zou kunnen dat 'PrintPoster' 2015 en 2016 heeft, terwijl 'PrintFolder' alleen 2015 heeft.

---

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// app/Calculators/PrintPoster/PrintPoster2015.php

namespace App\Calculators\PrintPoster;

use App\Calculators\PrintPosterInterface;

class PrintPoster2015 implements CalculatorInterface
{

    public function result($arguments)
    {
        return 1337;
    }

}


De implementatie voor 2015 van PrintPoster, volgens de CalculatorInterface.

---

PHP:
1
2
3
4
5
6
7
8
9
10
// app/Calculators/CalculatorInterface.php

namespace App\Calculators;

interface CalculatorInterface
{

    public function result($arguments);

}


De calculator interface, die geld voor alle 'types' en alle jaren.

---

Zelf heb ik Calculator::make('PrintPrice', $year)->result($data); overwogen, maar het type calculator als Facade lijkt me beter. Zoiets als PrintPoster::make($year)->result($data); ofzo.

Laravel heeft dat wel mooie oplossingen voor volgens mij, maar ik weet niet hoe ik ze gebruik. Hoe dan ook moet het geen PrintPoster2015:: of PrintPosterFactory:: worden in de code volgens mij.

Acties:
  • 0 Henk 'm!

  • TheNephilim
  • Registratie: September 2005
  • Laatst online: 09-09 12:00
Goed... misschien ben ik er al uit! Maar even verder in een nieuwe reactie, gezien het anders wel wat onduidelijk word.

App/Providers/CalculatorServiceProvider.php

PHP:
1
2
3
$this->app->singleton('calculators.printposter', function () {
    return new PrintPosterFactory();
});


Daarnaast heb ik in app/config/app.php de serviceprovider en alias toegevoegd.

---

App/Calculators/CalculatorInterface.php

PHP:
1
2
3
4
5
6
7
8
namespace App\Calculators;

interface CalculatorInterface
{

    public function result($arguments);

}


---

App/Calculators/PrintPoster/PrintPosterFacade.php

PHP:
1
2
3
4
5
6
7
8
9
10
11
namespace App\Calculators\PrintPoster;

use Illuminate\Support\Facades\Facade;

class PrintPosterFacade extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'calculators.printposter';
    }
}


---

App/Calculators/PrintPoster/PrintPosterFactory.php

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace App\Calculators\PrintPoster;

use App\Calculators\PrintPoster\PrintPoster2015;

class PrintPosterFactory
{
    public static function make($year)
    {
        switch ($year) {

            case 2015:
                return new PrintPoster2015();

            default:
                throw new \Exception('Unknown implementation!');
        }
    }
}



---

Tot slot...

PHP:
1
PrintPoster::make(2015)->result(['quantity' => 50]);


Dat werkt, tot zover in ieder geval. Misschien ben ik er zo, maar ik ga er nog even verder mee. Dit is even een 'basis' voor meerdere calculators die er nog in moeten.