0 follower

Automatyczne generowanie kodu

Poczynając od wersji 1.1.2, Yii wyposażone jest w przeglądarkowe narzędzie do generowania kodu Gii. Zastępuje ona poprzednie narzędzie do generacji yiic shell, które uruchamiane jest z linii poleceń. W tej części opiszemy jak używać Gii i jak rozszerzać Gii by zwiększyć produktywność dewelopmentu.

1. Używanie Gii

Gii zaimplementowane zostało w postaci modułu i musi być używane wewnątrz istniejącej aplikacji Yii. Aby używać Gii, najpierw modyfikujemy konfigurację aplikacji w następujący sposób:

return array(
    ......
    'modules'=>array(
        'gii'=>array(
            'class'=>'system.gii.GiiModule',
            'password'=>'wprowadź tutaj hasło',
            // 'ipFilters'=>array(...lista adresów IP...),
            // 'newFileMode'=>0666,
            // 'newDirMode'=>0777,
        ),
    ),
);

W powyższym kodzie deklarujemy moduł nazwany gii którego klasą jest GiiModule. Określamy również hasło dla modułu, które będzie rządane podczas uzyskiwania dostępu do Gii.

Domyślnie, z powodu bezpieczeństwa, Gii skonfigurowane jest jako dostępne na lokalnym komputerze. Jeśli chcemy udostępnić go dla innych zaufanych komputerów, możemy skonfigurować właściwość GiiModule::ipFilters w sposób pokazany w powyższym kodzie.

Ponieważ Gii może generować i zapisywać nowe pliki z kodem w istniejącej aplikacji, musimy się upewnić, że proces serwera ma odpowiednie uprawnienia do tego. Powyższe właściwości GiiModule::newFileMode oraz GiiModule::newDirMode kontrolują jak nowe pliki i katalogi powinny być wygenerowane.

Uwaga: Gii jest przewidziane jako narzędzie deweloperskie. Dlatego też, powinno być zainstalowane jedynie na maszynie deweloperskiej. Ponieważ może ono generować nowe skrypty PHP w aplikacji, powinniśmy zwracać dostateczną uwagę na środki bezpieczeństwa (np. hasła, filtry IP).

Możemy uzyskać dostęp do Gii poprzez adres URL http://hostname/sciezka/do/index.php?r=gii. Zakładamy, że http://hostname/sciezka/do/index.php to URL dostępu do istniejącej aplikacji Yii.

Jeśli istniejąca aplikacjia Yii używa formatu URL ścieżek (zobacz zarządzanie URL-ami), możemy uzyskać dostęp do Gii poprzez adres URL http://hostname/sciezka/do/index.php/gii. Możemy dodać następujący reguły URL na początej istniejących reguł URL:

'components'=>array(
    ......
    'urlManager'=>array(
        'urlFormat'=>'path',
        'rules'=>array(
            'gii'=>'gii',
            'gii/<controller:\w+>'=>'gii/<controller>',
            'gii/<controller:\w+>/<action:\w+>'=>'gii/<controller>/<action>',
            ...dotychczasowe reguły...
        ),
    ),
)

Gii dostarczane jest z kilkoma domyślnymi generatorami kodu. Każdy generator kodu jest odpowiedzialny za generowanie określonego typu kodu. Na przykład, generator kontrolera generuje klasę kontrolera wraz z kilkoma skryptami widoków akcji; generator modelu generuje klasę rekordu aktywnego dla określonej tabeli bazy danych.

Kolejne kroki używane podczas generowania wyglądają następująco:

  1. Wejdź na stronę generatora;
  2. Uzupełnij pola, które określają parametry generacji kodu. Na przykład, aby używać generatora modułu do utworzenia nowego modułu, należy podać ID modułu.
  3. Naciśnij przycisk podglądu kodu Preview, który zostanie wygenerowany. Pokaże się tabela przedstawiająca listę plików z kodem, które mają zostac wygenerowane. Możesz kliknąć na każdej z pozycji tej listy aby podejrzeć kod.
  4. Naciśnij przycisk generuj Generate aby wygenerować pliki z kodem;
  5. Przejrzyj log po generacji kodu.

2. Rozszerzanie Gii

Chociaż domyślne generatory kodu dostarczane z Gii mogą generować bardzo potężny kod, często chcemy dostosować go lub też utworzyć nowy zgodny z naszym gustem i potrzebami. Na przykład, możemy chcieć wygenerować kod, który będzie pisany w naszym ulubionym stylu kodowania lub też możemy chcieć dodać wsparcie dla wielu języków. Wszystko to może zostać zrobione przy użyciu Gii.

Gii może zostać rozszerzone na dwa sposoby: dostosowanie szablonów kodu istniejących generatorów kodu oraz napisanie nowych generatorów kodu.

Struktura generatora kodu

Generator kodu przechowywany jest w katalogu, którego nazwa jest traktowana jak nazwa generatora. Generator zazwyczaj zawiera następującą zawartość:

model/                       katalog główny generatora modelu
   ModelCode.php             model kodu używany do generowania kodu
   ModelGenerator.php        kontroler generujący kod
   views/                    zawiera skrypty widoku dla generatora
      index.php              domyślny widok skryptu
   templates/                zawiera zestawy szablonów kodu
      default/               'domyślny' zestaw szablonu kodu
         model.php           szablon kodu do generowania kodu klasy modelu

Ścieżka wyszukiwania generatorów

Gii szuka dostępnych generatorów w zestawie katalogów określonych przez właściwość GiiModule::generatorPaths. Jeśli potrzebne jest dostosowanie jej, możemy skonfigurować tą właściwość w konfiguracji aplikacji w następujący sposób:

return array(
    'modules'=>array(
        'gii'=>array(
            'class'=>'system.gii.GiiModule',
            'generatorPaths'=>array(
                'application.gii',   // alias ścieżki
            ),
        ),
    ),
);

Powyższa konfiguracja instruuje Gii aby szukać generatorów w katalogu o aliasie application.gii, w odróżnieniu od domyślnej lokalizacji system.gii.generators.

Możliwym jest posiadanie dwóch generatorów o tej samej nazwie ale pod różnymi ścieżakami wyszukiwania. W takim przypadku, generator znajdujący się w wyspecyfikowanej wcześnie właściwości GiiModule::generatorPaths ma pierwszeństwo.

Dostosowywanie szablonów kodu

Jest to najprostszy i najczęściej stosowany sposób rozszerzania Gii. Użyjemy przykładu w celu wyjaśnienia jak dostosować kod szablonu. Zakładamy, że chcemy dostoswać kod generowany przez generator modeli.

Najpierw tworzymy katalog o nazwie protected/gii/model/templates/compact. Tutaj model oznacza, że będziemy nadpisywać domyślny generator modelu. templates/compact oznacza, że dodamy nowy zestaw szablonów kodu o nazwie compact.

Następnie modyfikujemy naszą konfigurację aplikacji dodając application.gii do GiiModule::generatorPaths jak pokazano w poprzednim akapicie.

Teraz otwórz stronę generowania modelu. Kliknij na polu szablonu kodu Code Template. Powinniśmy zobaczyć listę rozwijaną, zawierającą nasz nowo utworzony katalog szablonów compact. Jednakże, jeśli wybierzemy ten szablon do generowania kodu, zobaczymy błąd. Dzieje się tak, pownieważ musimy jeszcze umieścić jakikolwiek właściwy plik szablonu kodu w nowym zestawie szablonów compact.

Skopiuj plik framework/gii/generators/model/templates/default/model.php do protected/gii/model/templates/compact. Jeśli spróbujemy ponownie wygenerować szablon compact zakończy się to sukcesem. Jednakże kod wygenerowany nie różni się od tego generowanego przez domyślny zestaw szablonów.

Nadszedł czas aby wykonać rzeczywista pracę dostosowywania. Otwórz plik protected/gii/model/templates/compact/model.php w celu edytowania go. Pamiętaj, że plik ten będzie używane jako skrypt widoku, co oznacza, że może on zawierać deklaracje i wyrażenia PHP. Zmodyfikujmy szablon w taki sposób, że metoda attributeLabels() w generowanym kodzie będzie używała Yii::t() do tłumaczenia etykiet atrybutów:

public function attributeLabels()
{
    return array(
<?php foreach($labels as $name=>$label): ?>
            <?php echo "'$name' => Yii::t('application', '$label'),\n"; ?>
<?php endforeach; ?>
    );
}

W każdym szablonie kodu możemy uzyskać dostep do pewnych predefiniowanych zmiennych takich jak $labels w powyższym przykładzie. Zmienne te są dostarczane przez odpowiednie generatory kodu. Różne generatory kodu mogą dostarczać różne zestawy zmiennych w swoich szablonach kodu. Prześledź uważnie opisy w domyślnym szablonie.

Tworzenie nowych generatorów

W tej części pokażemy jak utworzyć nowy generator, który może wygenerować nową klasę widżetu.

Najpierw utworzymy katalog o nazwie protected/gii/widget. W katalogu tym utworzymy następujące pliki:

  • WidgetGenerator.php: zawiera klasę kontrolera WidgetGenerator. Jest to punkt startowy generatora widżetów.
  • WidgetCode.php: zawiera klasę modelu WidgetCode. Klasa ta zawiera główną logikę generowania kodu.
  • views/index.php: skrypt widoku pokazujący formularz z polami wejściowymi generatora kodu.
  • templates/default/widget.php: domyślny kod szablonu do generowania pliku klasy widżetu.

Tworzenie WidgetGenerator.php

Plik WidgetGenerator.php jest maksymalnie prosty. Zwiera on tylko następujący kod:

class WidgetGenerator extends CCodeGenerator
{
    public $codeModel='application.gii.widget.WidgetCode';
}

W powyższym kodzie określamy jedynie to, iż generator będzie używał klasę modelu, której aliasem ścieżki jest application.gii.widget.WidgetCode. Klasa WidgetGenerator dziedziczy z klasy CCodeGenerator, która implementuje wiele funkcjonalności włączając w to akcje potrzebne do zarządzania procesem generowania kodu.

Tworznie WidgetCode.php

Plik WidgetCode.php zawiera model klasy WidgetCode, który zawiera główną logikę do generowania klasy widżetu opartej na danych wprowadzonych przez użytkownika. W przykładzie tym zakładamy, że jedyną daną jaką rządamy od użytkownika jest nazwa klady widżetu. Nasza klasa WidgetCode wygląda następująco:

class WidgetCode extends CCodeModel
{
    public $className;
 
    public function rules()
    {
        return array_merge(parent::rules(), array(
            array('className', 'required'),
            array('className', 'match', 'pattern'=>'/^\w+$/'),          
        ));
    }
 
    public function attributeLabels()
    {
        return array_merge(parent::attributeLabels(), array(
            'className'=>'Nazwa klasy widżetu',
        ));
    }
 
    public function prepare()
    {
        $path=Yii::getPathOfAlias('application.components.' . $this->className) . '.php';
        $code=$this->render($this->templatepath.'/widget.php');
 
        $this->files[]=new CCodeFile($path, $code);
    }
}

Klasa WidgetCode dziedziczy z CCodeModel. Tak jak w zwykłej klasie modelu także w tej możemy zadeklarować reguły rules() oraz etykiety atrybutów attributeLabels() odpowiednio w celu sprawdzenia danych wprowadzanych przez użytkownika oraz dostarczenia etykiet atrybutów. Zauważ, że klasa bazowa CCodeModel definiuje już pewne reguły oraz etykiety atrybutów i powinniśmy połączyć je z naszymi nowymi regułami i etykietami w naszej klasie.

Metoda prepare() przygotowuje kod, kóry będzie generowany. Jej głównym zadaniem jest przygotować listę obiektów CCodeFile, z których każdy reprezentuje plik kodu, który zostanie wygenerowany. W naszym przykładzie musimy jedynie utworzyć obiekt CCodeFile, który reprezentuje plik klasy widżetu, który zostanie wygenerowany. Nowa klasa widżetu zostanie wygenerowana w katalogu protected/components. Wywołujemy metodę CCodeFile::render w celu wygenerowania aktualnego kodu. Metoda ta dołącza kod szablonu jako skrypt PHP i zwraca jego zawartość jako wygenerowany kod za pomocą polecenia echo.

Tworznie views/index.php

Posiadając kontroler (WidgetGenerator) i model (WidgetCode) naszedł czas aby utworzyć widok views/index.php.

<h1>Generatow widżetu</h1>
 
<?php $form=$this->beginWidget('CCodeForm', array('model'=>$model)); ?>
 
    <div class="row">
        <?php echo $form->labelEx($model,'className'); ?>
        <?php echo $form->textField($model,'className',array('size'=>65)); ?>
        <div class="tooltip">
            Nazwa klasy widżetu musi zawierać jedynie litery i cyfry.
        </div>
        <?php echo $form->error($model,'className'); ?>
    </div>
 
<?php $this->endWidget(); ?>

W powyższym przykładzie wyświetlamy przede wszystkim formularz używając widżetu CCodeForm. W formularzu tym wyświetlamy pole zbierające dane wprowadzone dla atrybutu className klasy WidgetCode.

Podczas tworzenia formularza, możemy wykorzystać dwie fajne funkcjonalności dostarczone przez widżet CCodeForm. Pierwszą są podpowiedzi. Druga to przyklejone dane wejściowe (ang. sticky inputs).

Jeśli wypróbowałeś jakikolwiek z domyślnych generatorów kodu, zauważyłeś że podczas ustawiania fokusu na danym polu wejściowym, tuż obok pola wyświetli się ładna dla oka podpowiedź. Może to zostać uzystkane w bardzo prosty sposób poprzez napisanie za polem wejściowym tagu div, którego klasą CSS jest tooltip.

Dla pewnych pól wejściowych możemy chcieć zapamiętać ich ostatnią poprawną wartość tak aby uchronić użytkownika przed kłopotliwym powtarzaniem za każdym razem tych samych wartości podczas używania generatora. Przykładem jest pole odczytujące nazwę klasy bazowej domyślnego generatora kodu. Te przyklejone pola są początkowo wyświetlane jako podświetlony, statyczny tekst. Jeśli na nim klikniemy, zamieni się on na pole wejściowe pobierające daną wejściową od użytkownika.

W celu zdefiniowana pola wejściowego jako przyklejonego, potrzebujemy zrobić dwie rzeczy.

Najpierw potrzebujemy zadeklarować regułę sprawdzania poprawności sticky do odpowiedniego atrybutu modelu. Na przykład, generator domyślnego kontolera posiada następujące reguły deklarujące atrybuty klasy bazowej baseClass oraz akcji actions jako przyklejone:

public function rules()
{
    return array_merge(parent::rules(), array(
        ......
        array('baseClass, actions', 'sticky'),
    ));
}

Po drugie, potrzebujemy dodać klasę CSS o nazwie sticky do kontenera div pola wejściowego w widoku w następujący sposób:

<div class="row sticky">
    ...pola wejściowe...
</div>

Tworzenie templates/default/widget.php

Na koniec tworzymy szablon kodu templates/default/widget.php. Tak jak napisaliśmy już wcześniej, jest on używany jako skrypt widoku, który może zawierać wyrażenia i deklaracje PHP. W szablonie kodu możemy zawsze mieć dostęp do zmiennej $this, która odnosi się do obiektu modelu kodu. W naszym przykładzie $this odnosi się do obiektu WidgetModel. To pozwala uzyskać wprowadzoną przez użytkownika nazwę klasy poprzez $this->className.

<?php echo '<?php'; ?>
 
class <?php echo $this->className; ?> extends CWidget
{
    public function run()
    {
 
    }
}

Na tym kończy się tworzenie nowego generatora kodu. Możemy uzyskać dostęp do tego generatora kodu bezpośrednio poprzez adres URL http://hostname/sciezka/do/index.php?r=gii/widget.

Found a typo or you think this page needs improvement?
Edit it on github !