Autoload, namespace i nazwy klas w podkatalogach

0

Postanowiłem poznać nieco Laravela w wersji 4 i napotkałem na pewien problem, o którym niewiele znalazłem na sieci. Większość wątków odnosi się albo do własnych pakietów w /vendor i używania composer.json albo do kontrolerów w podkatalogach.

W głównym katalogu aplikacji app stworzyłem sobie katalog providers. W nim mam 2 pliki:

  • ProviderInterface.php
  • ProviderBaseClass.php
    a poza tymi plikami chcę umieścić klasy w podkatalogach, które dziedziczą i implementują te dwa pliki. Niestety, nie jestem jeszcze zbyt biegły w przestrzeniach nazw, których do tej pory w PHP niewiele używałem i jeszcze nie do końca je czuję.

Testowałem wiele różnych wariantów, ale nie udało mi się poprawnie załadować klasy - dostaję albo błędy o braku klasy docelowej albo o braku klasy podstawowej, albo ścieżki do nich są błędne.

Jedna z wersji wygląda tak:
/app/providers/ProviderInterface.php:

<?php
namespace Providers;  //czy to potrzebne?

interface ProviderInterface {
...

/app/providers/ProviderBaseClass.php:

<?php
namespace Providers;  //czy to potrzebne?

class ProviderBaseClass implements ProviderInterface {
...

No i przykładowy plik w przykładowym podkatalogu:
/app/providers/pl/foo.php:

<?php
namespace Pl; //tu próbowałem i Providers/Pl i tak jak jest; próbowałem dodawać też "use Providers;"

class Foo extends \Providers\ProviderBaseClass implements \Providers\ProviderInterface {  //tu próbowałem różnych opcji - bez \ na początku szuka w podkatalogu, czyli próbuje wczytać Pl/Providers/ProviderBaseClass
...

No i tworzenie klasy docelowej - tu też nie do końca czuję jaka wersja jest poprawna - testowałem między innymi:

new \Providers\Pl\Foo();
new Providers\Pl\Foo();
new \Pl\Foo();
new Pl\Foo();
new Foo();

Docelowo nazwę będę miał w stringu co jeszcze mocniej utrudnia. Będę miał np. $class = 'pl.foo';

Nie jestem pewien czy powinienem próbować tworzyć instancję w taki sposób, czy najpierw nie powinienem jakoś powiązać jej z IoC:

App::bind('Provider', function() {return new Pl/Foo();});

Jakby ktoś miał dobry i łopatologiczny opis przestrzeni nazw w PHP, to bym chętnie przyswoił. Tyle lat ich nie było w tym języku, że to taki mały przewrót dla mnie :)

[edited]
Byłbym zapomniał - w /app/start/global.php dodałem:

ClassLoader::addDirectories(array(
...
	app_path().'/providers',
));

Dodałem również w composer.json:

	"autoload": {
		"psr-0": {
			"Providers": "app/providers/"
		},

Ale nie jestem pewien tak prawdę mówiąc ile rzeczy jest zbędnych. Za dużo magii jak dla gościa wychowanego na Assemblerze ;-)

[edit2]
Czyżbym znalazł odpowiedź?
Zamieniłem psr-0 na psr-4:

	"autoload": {
		"psr-4": {
			"Providers\\": "app/providers/"
		},

I jestem w stanie stworzyć instancję klasy:

$providerName = 'pl\\foo';
$provider = new $providerName();

Klasa ma taką definicję:

<?php
namespace Pl;
use Providers;

class Foo extends Providers\ProviderBaseClass implements Providers\ProviderInterface {

Czy takie podejście jest prawidłowe?

1

Ogólnie psr-4 autoloading jest dosyć prosty do zrozumienia :). Tutaj filmik, który wszystko wyjaśnia:

https://laracasts.com/lessons/psr-4-autoloading

Nie zapomnij o composer dump-autoload. Nie ma w tym, jakiejś wielkiej magii, composer tworzy plik z tablicą która mapuje przestrzenie nazw na katalogi. Później ten plik jest wykorzystywany gdzieś tam przez framework wewnętrznie pewnie.

Nie wiem dlaczego chcesz tworzyć obiekty pewnych klas z nazwy która trzymana jest w stringu ale to już inna bajka :)

Konwencja wygląda tak:

{
    "autoload": {
        "psr-4": { "Gaua\\": "app/Gaua" }
    }
}
 
-app
  -Gaua
    -Foo
      -A.php
    -Bar
      -B.php
<?php
//B.php

namespace Gaua/Bar;

use Gaua/Foo/A;

class B extends A
{
    public function getParentObject()
    {
         return new A();
    }
}
 
0
gaUa69 napisał(a):

Nie wiem dlaczego chcesz tworzyć obiekty pewnych klas z nazwy która trzymana jest w stringu ale to już inna bajka
Bo to user selectem na stronie wybiera dostawcę (a co za tym idzie, klasę, która go obsłuży).

Czyli doszedłem do praktycznie takiej samej konwencji jak pokazałeś z tą różnicą, że u mnie jest:

<?php
namespace Pl;  //tutaj Ty masz Providers/Pl
use Providers;  //tutaj masz Providers\ProviderBaseClass

class Foo extends Providers\ProviderBaseClass implements Providers\ProviderInterface {

Czy obie wersje są poprawne? Bo generalnie kod już działa, ale skoro już robię to warto by było tip top :)

A ten filmik wisi w to-watch ;-)

1
Marooned napisał(a):
gaUa69 napisał(a):

Nie wiem dlaczego chcesz tworzyć obiekty pewnych klas z nazwy która trzymana jest w stringu ale to już inna bajka
Bo to user selectem na stronie wybiera dostawcę (a co za tym idzie, klasę, która go obsłuży).

Jakaś fabryka by tutaj pasowała, która tworzy obiekt odpowiedniego rodzaju. Nie znam się jakoś super ale chyba takie tworzenie obiektu dynamicznie ze stringa nie jest zbyt dobre.

namespace Pl; //tutaj Ty masz Providers/Pl

nie mogę stwierdzić co powinieneś mieć tutaj, to zależy od struktury plików w twoim projekcie.

use Providers; //tutaj masz Providers\ProviderBaseClass

taka konwencja importowania jest najbardziej popularna według mnie, dalej w kodzie nie musisz poprzedzać nazw klas przedrostkami, no chyba, że masz jakieś konflikty. Tak było by lepiej raczej:

 
use Providers\ProviderBaseClass;
use Providers\ProviderInterface ;

//use Providers;  z tym tez powinno ruszyc

class Foo extends ProviderBaseClass implements ProviderInterface {

1 użytkowników online, w tym zalogowanych: 0, gości: 1