Nest Starter cz. III

Po zdefiniowaniu wymagań pora zabrać się za ich implementacje. Jednak przed tym krokiem możemy sobie pracę ułatwić odpalając komendę bin/behat –apend-snippets. Komenda to dodaje wszystkie potrzebne definicje kroków potrzebnych do przetestowania wymagań.

Okej gdy szkielet klasy FeatureContext.php mamy gotowy to pora zabrać się za implementacje. W tym wpisie zajmiemy się content_page.feature.

Konfiguracja PhpSpec

Do tworzenia wszystkich klas użyjemy narzędzia PhpSpec, jednak aby ono generowało klasy tak jak sobie tego życzymy jest potrzebna odpowiednia konfiguracja. Najpierw najważniejszy plik czyli phpspec.yml:

formatter.name: pretty

suites:
    app:
        namespace: App
        src_path: .
    storage:
        namespace: Plugin\Storage
        src_path: Plugin/Storage
    webserver:
        namespace: Plugin\WebServer
        src_path: Plugin/WebServer

Plik szablonu generacji klas: (.phpspec/class.tpl)

<?php
/**
 * This file is part Nest Static Page application
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license    MIT
 */
namespace %namespace%;

/**
 * %namespace%\%name%
 *
 * @author Tomasz Ślązok <tomek@sabaki.pl>
 */
class %name%
{
}

Szablon generowanych specyfikacji (.phpspec/specification.tpl)

<?php
/**
 * This file is part Nest Static Page application
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license    MIT
 */
namespace %namespace%;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

/**
 * %namespace%\%name%
 *
 * @author Tomasz Ślązok <tomek@sabaki.pl>
 * @mixin  \%subject%
 */
class %name% extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType('%subject%');
    }
}

Static Page

Gdy PhpSpec jest skonfigurowany to pora na pierwszy UseCase. Odpalamy komende bin/phpspec describe App\\UseCase\\ViewStaticPage a następnie bin/phpspec run i odpwiadamy „Y” na zadane pytanie: Do you want me to create `App\UseCase\ViewStaticPage` for you?

Teraz pora przygotować specyfikacje klasy (jego API). ViewStaticPage bedzie wymagać przy stworzeniu Repository (obiekt za pomocą którego będą pobierane strony statyczne). Implementacja Repository nie jest istotna dla działania UseCase. Wymagane jest by Repository implementowało interfejs App\Repository\PageRepositoryInterface.

<?php
/**
 * This file is part Nest Static Page application
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license    MIT
 */
namespace App\Repository;

/**
 * Interface PageRepository
 *
 * @author  Tomasz Ślązok <tomek@landingi.com>
 */
interface PageRepositoryInterface
{
    public function findBySlug($slug);
}

Do zwrócenia strony będzie służyć metoda execute() która przyjmie jako parametr zmienna $slug. Natomiast metoda zwracać będzie w wyniku obiekt App\Entity\Page. Zaczniemy od specyfikacji. Tak wiec w pliku spec/App/UseCase/ViewStaticPageSpec.php dodajemy specyfikacje metody execute:

<?php
/**
 * This file is part Nest Static Page application
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license    MIT
 */
namespace spec\App\UseCase;

use PhpSpec\ObjectBehavior;

/**
 * spec\App\UseCase\ViewStaticPageSpec
 *
 * @author Tomasz Ślązok <tomek@sabaki.pl>
 * @mixin  \App\UseCase\ViewStaticPage
 */
class ViewStaticPageSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType('App\UseCase\ViewStaticPage');
    }

    function it_return_page_for_slug()
    {
        $this->execute('slug')->shouldReturnAnInstanceOf('App\Entity\Page');
    }
}

Nastepnie uzywamy komendy bin/phpspec run aby PhpSpec stworzył metodę execute() za nas. Niestety metoda execute jest pusta i zwraca null. Z tego powodu gdy odpalimy ponownie bin/phpspec run to otrzymamy komunikat o błędzie.

✘ return page for slug 
    expected an instance of App\Entity\Page, but got null. 

Specyfikacja wymaga aby metoda zwróciła entity Page. Nie ma jeszcze takiej klasy więc szybko ją tworzymy za pomocą phpspeca:

> bin/phpspec describe App\\Entity\\Page
Specification for App\Entity\Page created in /home/nexik/projects/nest/nest-static/spec/App/Entity/PageSpec.php.

> bin/phpspec run                                                                        
  Do you want me to create `App\Entity\Page` for you? [Y/n] Y

Class App\Entity\Page created in /home/nexik/projects/nest/nest-static/App/Entity/Page.php.

Narazie nie interesuje nas implementacja klasy Page, tak więc możemy wrócić do definicji metody ViewStaticPage::execute(). Tworzymy kod który spełni wymagania naszej wcześniejszej specyfikacji:

<?php
/**
 * This file is part Nest Static Page application
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license    MIT
 */
namespace App\UseCase;

use App\Entity\Page;

/**
 * App\UseCase\ViewStaticPage
 *
 * @author Tomasz Ślązok <tomek@sabaki.pl>
 */
class ViewStaticPage
{
    public function execute($slug)
    {
        return new Page();
    }
}

Po tej zmianie dostajemy zielone światło, jednak nie jest to końcowy efekt jaki byśmy chcieli, dlatego musimy zmodyfikować trochę specyfikacje. Klasa ViewStaticPage nie powinna być odpowiedzialna za tworzenie obiektu Page. Jak już wcześniej pisałem, zadanie to należy wydelegować do obiektu Repository. Dodajemy odpowiednią zależność do specyfikacji:

<?php
/**
 * This file is part Nest Static Page application
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license    MIT
 */

namespace spec\App\UseCase;

use App\Entity\Page;
use App\Repository\PageRepositoryInterface as Repository;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

/**
 * spec\App\UseCase\ViewStaticPageSpec
 *
 * @author Tomasz Ślązok <tomek@sabaki.pl>
 * @mixin  \App\UseCase\ViewStaticPage
 */
class ViewStaticPageSpec extends ObjectBehavior
{
    function let(Repository $repository)
    {
        $this->beConstructedWith($repository);
    }

    function it_return_page_for_slug(Repository $repository)
    {
        $repository->findBySlug('slug')->willReturn(new Page());

        $this->execute('slug')->shouldReturnAnInstanceOf('App\Entity\Page');
    }
}

Dokonaliśmy kilka ciekawych rzeczy.

  • Najpierw usuneliśmy specyfikacje it_is_initializable, ponieważ jest to domyślna specyfikacja która po stworzeniu klasy ma bardzo małą wartość. Czym mniej niepotrzebnego kodu tym lepiej.
  • Następnie uzyliśmy w specyfikacji metody let(). Metoda ta służy do przygotowania obiektu którego testujemy. Metoda let() służy także jako specyfikacja constructora.
  • W metodzie let skorzystaliśmy z wewnętrznego mechanizmu mockowania jaki dostarcza PhpSpec. System mockowania służy do uproszczenia budowy zależności pomiędzy obiektami. Podczas budowania obiektu repository nie interesuje nas jego implementacja ważne tylko żeby implementował odpowiedni interfejs
  • W metodzie let() zdefiniowaliśmy zachowanie $repository->findBySlug(). Jeżeli zostanie przekazany jako parametr string slug to repository zwróci obiekt Page
  • W specyfikacji it_return_page_for_slug() zdefiniowalismy zachowanie repository dla wywolania findBySlug(‚slug’)

Po tych zmianach gdy odpalimy bin/phpspec run to dostaniemy komunikat o błędzie:

> bin/phpspec run
✘ return page for slug
   some predictions failed:
      Double\PageRepositoryInterface\PageRepositoryInterface\P1:
        No calls been made that match:
          Double\PageRepositoryInterface\PageRepositoryInterface\P1->findBySlug(exact("slug"))
        but expected at least one.

Musimy zaktualizować metodę ViewStaticPage::execute() aby korzystała z repository:

<?php
/**
 * This file is part Nest Static Page application
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license    MIT
 */
namespace App\UseCase;

use App\Entity\Page;
use App\Repository\PageRepositoryInterface as Repository;

/**
 * App\UseCaseViewStaticPage
 *
 * @author Tomasz Ślązok <tomek@sabaki.pl>
 */
class ViewStaticPage
{
    /**
     * @var \App\Repository\PageRepositoryInterface
     */
    private $repository;

    /**
     * Constructor
     *
     * @param Repository $repository
     */
    public function __construct(Repository $repository)
    {
        $this->repository = $repository;
    }

    /**
     * Get static page
     *
     * @param $slug
     * @return \App\Entity\Page
     */
    public function execute($slug)
    {
        return $this->repository->findBySlug($slug);
    }
}

Teraz komenda bin/phpspec run zakończy się sukcesem. Trochę się nazbierało, następnym razem zabierzemy się za implementacje Repository dla stron statycznych w oparciu o pliki yaml a potem już tylko krok do napisania testów funkcjonalnych do Behata.

8188678495_05ef61c49a_b

Nest Starter cz. II

Po ustawieniu podstawowej struktury katalogów dla projektu prostej strony internetowej, pora na zdefiniowanie podstawowej funkcjonalności. W tym celu tak jak obiecałem ostatnio użyjemy Behata.

Zaczniemy od zainicjowania środowiska do testów BDD które dostarcza Behat. W tym celu należy urchomić skrypt z parametrem –init:

bin/behat --init
+d features - place your *.feature files here
+d features/bootstrap - place your context classes here
+f features/bootstrap/FeatureContext.php - place your definitions, transformations and hooks here

Jak widać Behat stowrzył dwa katalogi. Opis naszych historyjek będziemy przechowywać w katalogu features. Na potrzeby projektu przygotowałem dwa pliki .features. Jeden dla obsługi stron statycznych oraz jeden do obsługi formularza kontaktowego.

/features/content-page.feature

Feature: Content Page
    In order to get information about company
    As a Visitor
    I need to be able to see pages on the company website

    Scenario:
        Given I am on the home page
        Then I see page title: "Homepage"
        And I see page content: "Welcome on our homepage"

    Scenario:
        Given I am on the about page
        Then I see page title: "About Us"
        And I see page content: "We are very nice company"

    Scenario:
        Given I am on the contact page
        Then I see page title "Contact"
        And I see page content: "We are happy hear from you. Please use form to contact us"

/features/contact.feature


Feature: Contact
    In order to ask question to the company
    As a Visitor
    I need to able to submit contact form

    Scenario:
        Given I am on the contact page
        When I fill in the following:
            | email   | me@domain.com            |
            | message | I want just to say hello |
        And I press "Send message"
        Then I see "Your message has been sent!"

Gdy mamy wymagania przygotowane, to następnym razem pokaże jak zabrać się za pisanie testów i implementacji w PHP.

Źródło zdjęcia: https://www.flickr.com/photos/kalleboo/8188678495

2685458942_43202ea04c_o

Nest Starter cz. I

Napisanie aplikacji w PHP która będzie rozwijana nie jest prostym zadaniem. Zawsze gdy zaczyna się nowy projekt to wszyscy są podekscytowani że tym razem wszystko będzie pięknie, czysto i wspaniale. Niestety w większości przypadków wszystko kończy się tak samo. Mamy kod który wcale nie jest taki prosty w utrzymaniu. Terminy zaczynają gonić i kod się pogarsza i za każdym razem jak coś dodajemy do aplikacji prace nad nią stają się coraz trudniejsze. Dlaczego tak jest?

Odpowiedź jest w miarę prosta: Jeżeli na początku wszyscy są podekscytowani i do tego nie mają dyscypliny TDD to szybkie wdrażanie nowych funkcji powoduje że architektura staje się coraz to gorsza.

Rozwiązanie jest proste:

  • Zanim rozpoczniesz prace nad aplikacja przemyśl jej architekture.
  • Framework nie jest częścią aplikacji jest pluginem.
  • Przestrzeganie TDD. (3 zasady TDD)

Aby pokazać praktyczne rozwiązanie zademonstruje je na przykładzie prostej strony webowej. Strona bedzie się składać z 3 podstron (strony głównej, strony about, strony kontaktu).

Struktura projektu

bin/
features/
spec/
App/
    Boundary/
    Entity/
    Repository/
    UseCase/
Plugin/
    Storage/
    WebServer/
       Controller/
       Resources/
          assets/
             less/
             public/
          bin/
          cache/
          config/
          views/

Do załadowania wszystkich narzędzi potrzebnych do pracy użyjemy Composera. Oto podstawowy composer.json:

{
    "require": {
        "nexik/nest-core": "dev-master"
    },
    "require-dev": {
        "behat/behat":               "~3.0",
        "bossa/phpspec2-expect":     "*",
        "phpspec/phpspec":           "~2.0",
        "phpspec/prophecy":          "~1.0",
        "squizlabs/php_codesniffer": "~1.0"
    },
    "autoload": {
        "psr-4": {
            "App\\" : "App",
            "Plugin\\": "Plugin"
        }
    },
    "config": {
        "bin-dir": "bin/"
    },
    "minimum-stability": "dev"
}

Szybkie wyjaśnienie struktury projektu:

  • App\ tutaj znajdzie się kod naszej aplikacji
  • App\UseCase\ tutaj znajdą się wszystkie przypadki użycia czy historyjki scrumowe.
  • App\Entity\ tutaj znajdą się podstawowe pojęcia aplikacji takie jak np strona statyczna
  • App\Repository\ tutaj znajdą się interfejsy w jaki sposób UseCase będzie mógł dobrać się do instancji Entity
  • App\Boundary\ tutaj znajdą się klasy DTO które będą przesyłane pomiędzy Pluginani a Aplikacją. Obiekty te zapewniają wspólny interfejs w komunikacji aplikacji z resztą świata.
  • Plugin\ tutaj znajdą się wszystkie pluginy związane z detalami. Tymi detalami są m.in Framework czy Baza danych
  • Plugin\Storage\ tutaj znajdą się implementacje interfejsów z App\Repository które będą zwracać potrzebne dane
  • Plugin\WebServer\ tutaj znajdzie się implementacja obsługi requestu HTTP który zostanie przetworzony na potrzeby danego UseCase’a oraz przyjęcie odpowiedzi z UseCase’a i przygotowanie strony HTML i wysłanie jej do klienta
  • Plugin\WebServer\ tutaj znajdzie się tak zwany Delivery Mechanism (framework)
  • Plugin\WebServer\Controller\ lista controllerów które będą reagować na request HTTP
  • Plugin\WebServer\Resources\ pliki nie przechowujacę klasy PHP
  • Plugin\WebServer\Resources\assets\ pliki potrzebne do przygotowania albo dostarczenia UI (html, css, js)
  • Plugin\WebServer\Resources\bin\ katalog do plikiem console przydatnym przy pracach nad projektem, zastosowanie np czyszczenie zawartosci katalogów cache
  • Plugin\WebServer\Resources\cache\ tutaj będą trzymane wszystko to co zajmuje za dużo czasu aby wykonywać za każdym requestem
  • Plugin\WebServer\Resources\config\ wszystkie pliki konfiguracyjne takie jak reguły routingu (routing.yml), główna konfiguracja (config.yml), definicje DI (services.yml)
  • Plugin\WebServer\Resources\views\ pliki templatek

Dodatkowo są katalogi które służą czysto tworzeniu aplikacji:

  • bin/ przydatne skrypty podczas pracy
  • features/ Testy BDD. Tutaj znajdą się opisy wszystkich użyć aplikacji, tak zwane Story.
  • spec/ Testy TDD. Tutaj się znajdą specyfikacje jak ma wyglądać API klas które mają później zostać stworzone.

Do implementacji projektu skorzystamy z:

Następnym razem pokaże jak zapisać wymagania aplikacji za pomocą Behata i jak rozpocząć pracę nad aplikacją nie myśląc jeszcze o Pluginach przy pomocy PHPSpec.

Źródło zdjęcia:  https://www.flickr.com/photos/blondie5000/2685458942

6988157282_d2c10fc165_o

DbInjectionAware formularz w Phalconie

Pisząc pewien projekt w Phalconie, natknąłem się na sytuację gdy formularz musiał mieć validator Uniqueness. Oczywiście taki validator istnieje, ale w Modelach: Phalcon\Mvc\Model\Validator\Uniqueness. Niestety postanowiłem nie korzystać z phalconowych modeli z powodu łamania zasady SOLID.

Pomysł

Wpadłem na pomysł aby stworzyć własny validator który by korzystał z połączenia Db i sprawdzał czy podana wartość już istnieje w bazie. Tutaj jednak pojawiło się pytanie w jaki sposób wstrzyknąć instancje połączenia z bazą danych do validatora?

Pierwszym pomysłem było skorzystać z Dependency Injection w Phalconie jako Service Locator. Jednak uważam że o ile można, to należy omijać Service Locatora. Z tego powodu postawiłem rozwiązać problem trochę okreżną drogą, ale za to łatwo testowalną.

Rozwiązanie

Rozwiązaniem jest połączenie własnego FormManagera oraz Interfejsu DbInjectionAwareInterface.

<?php
namespace My\App\Forms;

class Manager extends \Phalcon\DI\Injectable
{
    private $definitions;

    public function set($name, $definition)
    {
        if (is_string($definition)) {
            $definition = ['className' => $definition];
        }

        $this->definitions[$name] = $definition;
    }

    public function get($name)
    {
        if ($this->has($name)) {
            $className = $this->definitions[$name]['className'];
            $form = new $className();

            if ($form instanceof DbInjectionAwareInterface) {
                $form->setDb($this->getDI()->get('db'));
            }

            if (method_exists($form, 'configure') {
                $form->configure();
            }

            return $form;
        }
    }

    public function has($name)
    {
        return isset($this->definitions[$name]);
    }
}

Moja wersja Form Managera sprawdza przy tworzeniu instancji formularza czy implementuje DbInjectionAwareInterface, jeżeli tak to wstrzykuje db.

Dodatkowo jeżeli formularz posiada metodę configure to jest on odpalany przed zwróceniem obiektu formularza. Jest to spowodowane tym, że wbudowana metoda initialize() jest odpalana podczas konstruktora. W konstruktorze nie ma jeszcze dostępu do wstrzyknietej instancji db. Z tego powodu dodałem obsługę metody configure.

<?php
namespace My\App\Forms;

interface DbInjectionAwareInterface
{
    public function getDb();
    public function setDb($db);
}

Teraz wystarczy tylko odpowiednie zmiany w DI aplikacji

<?php

$di->set('formManager', 'My\App\Forms\Manager');
$formManager = $di->get('formManager');
$formManager->set('register', 'My\Forms\RegisterForm');

Oczywiście aby się nie powtarzać RegisterForm może extendować po klasie która ma zaimplementowany DbInjectionAwareInterface. Jednak ja napisałem do tego traitsa:

<?php
namespace My\App\Di;

trait DbInjectableTrait
{
    private $db;

    public function setDb($db)
    {
        $this->db = $db;
    }

    public function getDb()
    {
        return $this->db;
    }
}
<?php
namespace My\Forms;

use My\App\Di\DbInjectableTrait;
use My\App\Di\DbInjectionAwareInterface;
use Phalcon\Forms\Form;

class RegisterForm extends Form implements DbInjectionAwareInterface
{
    use DbInjectableTrait;

    public function configure()
    {
        //...
    }
}

Autor zdjęcia: Philip Taylor

4773457853_b10fcc8294_b

Automatyczny VirtualHost w Ubuntu

Jeżeli jesteś freelancerem albo developerem i pracujesz nad wieloma projektami webowymi to najprawdopodobniej masz następujący problem przy tworzeniu nowych projektów.

  • Stworzyć nowy katalog project-name w katalogu www
  • Dopisać nowa domenę do hostów w systemie: project-name.dev 127.0.0.1
  • Dopisać nowy vhost w konfiguracji apacha który powiąże project-name.dev => /www/project-name/public_html 

Każdy programista dąży do automatyzacji prostych zadań. Oto tutorial jak ten proces zautomatyzować.

Podpięcie domen *.dev na 127.0.0.1

Aby zautomatyzowanie podpinania domen pod localhosta potrzebne jest dodatkowo oprogramowanie. Mowa tu Dnsmasq. Instalacja:

sudo apt-get install dnsmasq

Następnie edytujemy plik /etc/dnsmasq/dnsmasq.conf i dodajemy nasŧępujące linijki na samym dole pliku

listen-address=127.0.0.1
address=/.dev/127.0.0.1

Restartujemy Dnsmasq następującym poleceniem

sudo service dnsmasq restart

GOTOWE. Teraz wszystkie adresy *.dev na naszym komputerze będą kierować na 127.0.0.1

VirtualHosty w Apache2

W tym celu potrzebny jest mod_vhost_alias. Podstawowa wersja Apache2 instalowana z paczek ubuntu powinno go już mieć, tak więc wystarczy go aktywować:

sudo a2enmod vhost_alias
sudo service apache2 restart

Konfiguracja VirtualHost

Ostatnim krokiem jest konfigiracja Apache. Edytujemy /etc/apache2/sites-available/default

<VirtualHost *:80>
    VirtualDocumentRoot "/sciezka_do_www/%-2+/public_html"
    ServerName vhosts.dev
    ServerAlias *.dev
    UseCanonicalName Off

    <Directory /sciezka_do_www/*>
        Options Indexes FollowSymLinks
        AllowOverride All
        Order allow,deny
        allow from all
    </Directory>
# reszta konfiguracji
</VirtualHost>

Na koniec restart Apache i GOTOWE :)

Autor zdjęcia: Tom Raftery

3762360637_6b851c9478_b

User Settings dla Sublime Text 2

{
  "auto_complete_commit_on_tab": true,
  "color_scheme": "Packages/Tomorrow Color Schemes/Tomorrow-Night-Eighties.tmTheme",
  "draw_minimap_border": true,
  "font_face": "Ubuntu Mono",
  "font_options": ["subpixel_antialias"],
  "font_size": 11,
  "highlight_line": true,
  "ignored_packages": ["Vintage"],
  "line_padding_bottom": 2,
  "line_padding_top": 2,
  "rulers": [120],
  "scroll_past_end": false,
  "tab_size": 4,
  "theme": "Centurion Blue.sublime-theme",
  "translate_tabs_to_spaces": true,
  "trim_trailing_white_space_on_save": true,
  "wombat_color_blue": true,
  "wombat_dirty_bottom_bar_blue": true,
  "word_wrap": true,
  "wrap_width": 120
}

Autor zdjęcia: Eric Miraglia

3711363516_ca2338fbe6_b

Ubuntu 12.04 a polskie znaki

Po instalacji Ubuntu 12.04 nie działały mi polskie znaki: ą,ę etc.

Problem okazał się banalnie prosty, jednak znaleźienie rozwiązania już nie tak bardzo. Z tego powodu też publikuje ten wpis, może komuś się przyda.

  1. Uruchamiamy System Settings > Keyboard.
  2. Na samym dole okienka jest link Layout Settings, klikamy w niego.
  3. Upewniamy się czy mamy dodany layout Polish. 
  4. Następnie klikamy w Options i pojawia się wypunktowana lista z masą opcji.
  5. Rozwijamy element: Key to choose 3rd level i wybieramy  Right Alt.
No i teraz za pomocą prawego alta możemy wpisywać ą,ę itp.
Autor zdjęcia: Nicolás Demarchi
monitors_large

Sublime Text 2 – Pluginy

Sublime Text 2 to genialny edytor tekstu. Od ponad pół roku używam go zamiast Netbeansa czy PHPStorma. Sam edytor tekstu jest super świetny, ale prawdziwa produktywność ma miejsce dopiero po zainstalowaniu paru pluginów.

1. SFTP

Plugin zbawienie przy projektach dla klientów, którzy nie wiedzą co to SSH (lub nie mają dostępu – zwykły hosting).

2. SidebarEnhancements

Sidebar w Sublime Text 2 to poprostu pomyłka, nic się nie da z nim zrobić. Dzięki pluginowi wreszcie sidebar działa normalnie.

3. SublimeLinter

Jeżeli dbasz o jakość kodu to SublimeLinter jest twoim najlepszym przyjacielem.

4. Tag

Doskonały pomocnik przy tworzeniu i refactoringu kodu HTML. Osobiście nieocenione narzędzie przy fixowaniu kodu na potrzeby newsletterów „zepsutego” przez WYSIWYGi.

5. Modific

Jeżeli używasz Svn’a lub Git’a to ten plugin zaznaczy ci linijki które zostały zmienione od ostatniego commita. Czasami prawdziwy „life saver”.

6. DocBlocker

Dobry helper przy tworzeniu komentarzy.

7. Goto Documentation

Jak nie pamiętasz kolejności parametrów w jakieś funkcji, ale co ona zwraca to ten plugin przeniesie Cię na odpowiednią stronę manuala.

8. Theme – Nil

Obecnie mój ulubiony wygląd do ST2. Dopasowuje nie tylko edytor, ale także cały interfejs edytora.

9. Inne

Kiedyś używałem wielu innych pluginów, ale okazało się że wprowadzają tylko szum. W taki sposób oduczyłem się korzystać z kilku narzędzi typowych dla IDE jak np.: autocomplete czy przechodzenie do definicji klasy. Na początku było trudno. Po 2 miesiącach jednak okazało się że nie potrzebuje IDE, a mój kod oraz wiedza o nim się mocno poprawiła. Zwiększyła się także produktywność.

Źródło zdjęcia: http://www.sublimetext.com/blog/articles/anatomy-of-a-next-generation-text-editor

5580657696_bf70c5a0f3_b

Sortowanie tablic po danej wartości indeksu

Czasem mamy dane w tablicach zamiast w bazie danych.

Przykład

Mamy listę użytkowników w kolekcji tablic. Każda tablica składa się z par klucza i wartości

<?php
array(
  'name' => 'Jaś Kowalski',
  'points' => 10
);

Gdy chcemy wyswietlic ranking użytkowników, to chcemy posortować naszą kolekcję tablic z danymi użytkownikach według wartości indeksu ‚points’. 

Rozwiązanie

<?php
$sort = [];

foreach ($collection as $key => $row) {
  $sort[] = $row['points'];
}

array_multisort($sort, SORT_DESC, $collection);

foreach jest w php dość wolny, więc nie polecam tego rozwiązania dla kolekcji danych z 10 000+ elementów. Ale wtedy napewno dane macie już w bazie danych i zrobicie prostego SELECT * FROM users ORDER BY points

4440561067_ff472f723c_b

Jak sortować tablice UTF-8 w PHP

Jeżeli mamy tablicę której kluczami są nazwy miejscowości (zakodowanych w UTF-8) i chcemy ją posortować alfabetycznie po kluczach to mamy problem. Zwykłe ksort( $array ); nie wystarczy :(

Najpierw musimy upewnić się że jest załadowany odpowiedni locale, a następnie użyć metody sortowania za pomocą funkcji użytkownika. A oto pełne rozwiązanie w PHP:

<?php
setlocale( LC_ALL , 'pl_PL.UTF-8' );
uksort( $array , 'strcoll' );

Została tutaj użyta funckja strcoll która porównuje dwa stringi według collate aktualnego locale.

Autor zdjęcia: Jessica Wilson