<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Petr Jirásek</title>
		<link>https://petrjirasek.cz/blog/</link>
        <description>Osobní blog o programování, o webových aplikacích a řadě dalších témat</description>
        <language>cs</language>
        <copyright>mail@petrjirasek.cz</copyright>
        <category>blog</category>
        <docs>http://backend.userland.com/rss</docs>
		<atom:link href="https://petrjirasek.cz/rss.xml" rel="self" type="application/rss+xml" />

		<item>
            <title>Moje první dojmy se službou CloudFlare</title>
            <link>https://petrjirasek.cz/blog/moje-prvni-dojmy-se-sluzbou-cloudflare</link>
            <guid>https://petrjirasek.cz/blog/moje-prvni-dojmy-se-sluzbou-cloudflare</guid>
            <comments>https://petrjirasek.cz/blog/moje-prvni-dojmy-se-sluzbou-cloudflare#disqus_thread</comments>
            <description>
CloudFlare je služba poskytující
ochranu před různými druhy útoků pro běžné webové stránky, přičemž
nabízí i možnosti CDN a další možnosti optimalizace.

Instalace služby je velice pohodlná, víceméně se zaregistrujete,
vložíte doménu webové stránky, CloudFlare si zanalyzuje DNS záznamy a ty
převede k sobě. Poté vám řekne, jaký máte nově nastavit primární a
sekundární DNS server, což jsou servery CloudFlare, které pak zpracovávají
požadavky.

Jakmile se vámi provedená změna projeví, tak v tu chvíli můžete
využívat výhod CloudFlare, kde kromě ochrany před běžnými útoky je
lákavá hlavně podpora CDN a cachování. Prakticky se nemusíte o nic starat
a CloudFlare analyzuje provoz tak, že statické zdroje jako jsou obrázky,
styly nebo js soubory si cachuje a poté servíruje přímo uživateli, aniž by
request uživatele na daný zdroj směřoval na samotný server.

Další zajímavou funkcí je, že pokud váš server vrací status 500, pak
CloudFlare zobrazuje cachovanou verzi webu, kterou dle vybraného plánu jednou
za čas aktualizuje. Mimochodem, pokud jste fanoušky The Pirate Bay, tak tam
jsem už párkrát viděl, že CloudFlare vytáhl cachovanou verzi v omezeném
režimu.

CloudFlare má v základu free
plan, který pro malé weby zcela stačí. Sám jsem ho prvně testoval na
tomto blogu, kde v pořádku funguje. Free plan není omezen trafficem, jak by
člověk čekal, takže může být klidně použit i pro velké weby, za
předpokladu, že nepoužíváte HTTPS.

Pokud totiž máte HTTPS a chcete používát CloudFlare, mohou si začít
někteří uživatelé vaší služby stěžovat, že se nedostanou na váš
web. Tito uživatelé pravděpodobně používájí Windows XP a příčina
plyne z problému
s SNI. Na druhou stranu, to se dá vyřešit placeným tarifem, který je
schopen problém vyřešit a tarif ani není drahý. Vyjde na 20 dolarů
za měsíc.

S CloudFlare jsme začali experimentovat i na větším projektu, kde po
prvních porodních bolestech, právě s uživateli s WIndows XP, se služba
osvědčila a nyní nám slouží dobře. Ze statistik vychází, že nám
ušetří stovky tisíc requestů na server denně, s tím, že se jedná
v celkovém objemu o úsporu 90 %. Jen 10 % requestů směřuje na samotný
server. Podobně to vychází z pohledu přenesených dat, kde úspora je
v řádech jednotek GB dat denně.

Na závěr dvě poslední poznámky. Pokud CloudFlare začnete používat,
nezapomeňte, že poté veškeré DNS záznamy, DNSka pro subdomény nebo třeba
DKIM budete nastavovat v CloudFlare. Pokud budete mít na serveru umístěný
obrázek xyz.png a ten změníte se zachováním stejného názvu,
musíte invalidovat cache v CloudFlare (což je úkon na jeden klik), jinak
CloudFlare může uživatelům servírovat stále ten cachovaný. Zní to
samozřejmě naprosto jasně, ale raději to připomínám, ať si někdo
zbytečně neláme hlavu.
</description>
            <category></category>
            <category>development</category>
        </item>
		<item>
            <title>Vyhledávání jinak - zkuste Algolii</title>
            <link>https://petrjirasek.cz/blog/vyhledavani-jinak-zkuste-algolii</link>
            <guid>https://petrjirasek.cz/blog/vyhledavani-jinak-zkuste-algolii</guid>
            <comments>https://petrjirasek.cz/blog/vyhledavani-jinak-zkuste-algolii#disqus_thread</comments>
            <description>
Vyhledávání na webu, v eshopu, kdekoliv, je důležitou součástí
každého webu. Samotného mě dokáže naštvat, když vyhledávání nefunguje
tak, jak bych si představoval a místo mým přítelem se stane mým
nepřítelem.

Je až s podivem, kolik webů dnes stále používá nějaký naivní
způsob vyhledávání.

V zásadě jde často o to, že při vložení vyhledávacího dotazu do
formuláře se zavolá nějaký SELECT, kde se přes LIKE vyhledává záznam se
shodným podřetězcem. Každopádně tahle metoda je dost naivní a
neumožňuje spoustu věci, neřeší problémy s tím, že uživatelé píšou
třeba bez diakritiky, dělají překlepy a další aspekty jazyka uživatele.
Zároveň je dost komplikované řešit, pokud hledáte podvýraz třeba
v názvu produktu a v jeho popisu a upřednostnit pak ten výsledek, který
má lepší relevanci.

Ono relevance je obecně důležitá věc – vidíme to pořád a je to
alfa omega největších vyhledávačů. To samé platí pro největší eshopy
a jiné služby, které to myslí s prožitkem svých uživatelů při
použití jejich služby vážně.

Jak to řešit?

Vyhledávání lze řešit, jak už jsem psal, naivně nebo lépe
sofistikované. Když to myslíte vážně, použijete pravděpodobně
ElasticSearch, který lze použít nejen pro účely vyhledávání. Ten ale
dnes zmiňovat nechci, spíš bych chtěl zmínit i jinou alternativu. A teď
konečně k té Algolii z titulku.

Co je to ta Algolia?

Algolia je zahraniční služba, která má vlastně jen jeden cíl, dělat
dobře vyhledávání a pomáhat s tím, aby jste ho dobře dělali.
V zásadě jde o to, že do Algolie nahrajete záznamy, typický třeba feed
článků, produktů, …, které se spravují v indexu (tak se označuje
struktura, co záznamy spravuje). Nad indexem si pak naklikáte pravidla, jakým
způsobem se mají výskyty výrazu v jednotlivých atributech upřednostnit,
tj. jakou mají mít váhu, podle jaké priority se mají záznamy řadit a to
je vlastně v základu vše. Existuje tam pak i řada pokročilejších
nastavení jako, kolik překlepů má ve výrazů Algolia ignorovat, jak
citlivá má být apod.

Pak už jen skrze API posíláte vyhledávací dotazy do svého indexu
v Algolii a vracíte uživateli výsledky. Data můžete nahrávat manuálně
nebo přes API. Algolia disponuje řadou knihoven, pro různé platformy, takže
napojení je vcelku pohodlné. Vše je dobře zdokumentované, takže by neměl
být žádný problém.

Mé zkušenosti

Použil jsem Algolii v několika projektech a ušetřilo nám to spoustu
trápení. Na Tipli.cz to kupříkladu
používáme pro vyhledávání obchodů. Posíláme do Algolie data, upravujeme
ranking v administraci a výsledky pak jen vypisujeme. Nic víc vlastně
nemusíme řešit, pingy na jejich servery jsou dostatečně rychlé a lze si
vybrat nejbližší oblast, kde se servery nachází. Ted je to tuším
Německo.

Zároveň doporučují používat analytiku od Algolie, která dost dobře
reflektuje, co lidé hledají a najdou a naopak co lidé u vás hledají, ale
najít nemohou. Lze tak podle toho optimalizovat nejen strategii pro hledání,
ale také třeba samotnou produktovou nabídku.

Kolik to stojí?

Algolia disponuje free tarifem a poté placenými. V zásadě pro menší
web stačí free varianta, pro větší aplikaci je vhodné si pak připlatit.
Nejnižší placený tarif je $49 měsíčně.

Použití v Nette

Algolia má balíček i pro PHP. Nějakou dobu zpátky jsem vytvořil
jednoduchý balíček, co umožní vložit konfiguraci přes config v Nette.
balíček se pak stal oficiálním extension v repozitáři Algolie.

Balíček najdete zde.

Závěr

S Algolii mám dobře zkušenosti a nechci polemizovat nad tím, kdy je
vhodné ji použít oproti ElasticSearch apod. Oba řešení mají své místo a
záleží jen na vašem případu užití a chuti, které řešení zvolíte.
Každopádně na čem se shodneme asi všichni je skutečnost, že nerelevantní
vyhledávání je zlo.

Jaké máte vy zkušenosti s vyhledáváním? Klidně se podělte o své
zkušenosti.

Odkazy na závěr

Algolia web
Extension pro
Nette
PHP
knihovna
Dokumentace
pro PHP

Použití na VimVic.cz
Použití na Tipli.cz
</description>
            <category></category>
            <category>php</category>
            <category>development</category>
            <category>nette</category>
        </item>
		<item>
            <title>Jak na vlastní UserStorage v Nette</title>
            <link>https://petrjirasek.cz/blog/jak-na-vlastni-userstorage-v-nette</link>
            <guid>https://petrjirasek.cz/blog/jak-na-vlastni-userstorage-v-nette</guid>
            <comments>https://petrjirasek.cz/blog/jak-na-vlastni-userstorage-v-nette#disqus_thread</comments>
            <description>
Při pravidelném používání Nette můžete přijít na to, že vám
výchozí UserStorage, který uchovává v session informace
o uživateli, nemusí stačit nebo nevyhovuje. Typické problémy mohou být,
že pokud máte uloženy informace o uživateli jako třeba jméno,
příjmení, tak v případě, že uživatel si tyto údaje změní
v nastavení, tak logicky nedojde k jejich aktualizaci
v UserStorage, pokud tomu explicitně nepomůžete.

V tomto článečku bych proto chtěl napsat o tom, jak mít vlastní
UserStorage a co to vlastně vyžaduje. K závěru pak ukázka
řešení pro Doctrine.

UserStorage

UserStorage,
jak asi většina ví, je perzistentní uložistě pro uživatelská data,
které implementuje interface IUserStorage.
To definuje několik metod, jako třeba nastavení expirace uložiště nebo
nastavení identity uživatele, kterou uložiště drží.

Identita je pak třída reprezentující identitu uživatele a opět
implementuje předepsaný interface IIdentity.
Pokud chcete do UserStorage uložit jakoukoliv identitu uživatele, pak
stačí, aby tato identita respektovala interface IIdentity.
UserStorage je pak schopen s takovou identitou pracovat. Může to tak
tedy být třída Identity
z Nette nebo třeba entita z Doctrine reprezentujícího uživatele, která
bude implementovat interface IIdentity.

Taková Doctrine entita může vypadat třeba takto:

&amp;lt;?php

namespace petrjirasek\Entities\User

use DateTime;
use Doctrine\ORM\Mapping as ORM;
use Nette\Security\IIdentity;

/**
 * @ORM\Entity
 */
class User implements IIdentity
{
    const ROLE_MEMBER = &#039;member&#039;;
    const ROLE_ADMIN = &#039;admin&#039;;

    /**
     * @ORM\Id
     * @ORM\Column(type=&amp;quot;integer&amp;quot;)
     * @ORM\GeneratedValue
     */
    protected $id;

    /**
     * @ORM\Column(type=&amp;quot;string&amp;quot;, unique=true)
     */
    protected $email;

    /**
     * @ORM\Column(type=&amp;quot;string&amp;quot;)
     */
    protected $role = self::ROLE_MEMBER;

    /**
     * @ORM\Column(type=&amp;quot;string&amp;quot;)
     */
    protected $password;


    /**
     * @return int
     */
    public function getId()
    {
        return $this-&amp;gt;id;
    }

    /**
     * @return array
     */
    public function getRoles()
    {
        return [$this-&amp;gt;role];
    }

    ...
}

V případě volání metody getIdentity() nad uživatelem, třeba
v presenteru, pak dostaneme v případě přihlášeného uživatele vždy tu
identitu, kterou jsme si zvolili a při autentizaci uživatele nastavili. Opět
ukázka:

function authenticate($email, $password)
{
    /** @var User $user */
    $user = $this-&amp;gt;userRepository-&amp;gt;findByEmail($email);

    if ($user === NULL) {
        throw new AuthenticationException(&amp;quot;Uživatel neexistuje.&amp;quot;);
    }

    if (!$user-&amp;gt;verifyPassword($password, $this-&amp;gt;hasher)) {
        throw new AuthenticationException(&amp;quot;Nesprávné přihlašovací údaje.&amp;quot;);
    }

    return $user;
}

Zde nám return $user; vrátí naši entitu User
implementující IIdentity.

To by nám mohlo stačit, pokud by nám nevadilo, že v případě, že si
přihlášený uživatel změní v nastavení některý atribut (např. budeme
ukládat v entitě navíc jméno), se tato změna neprojeví v identitě,
pokud bychom neprovedli explicitně její aktualizaci po uložení
nastavení.

A to není jediná situace, změnu některého atributu může provést
i někdo jiný, třeba uživatelská podpora v adminu a určitě by bylo
vhodné, aby se změna v identitě uživatele projevila okamžitě.

Co s tím?

Jelikož je identita často obrazem identity uložené v jiném uložišti,
typicky v databázi, bylo by šikovné, kdybychom dostali vždy aktuální
identitu reflektující aktuální stav.

Málokomu se totiž chce psát kód podobný tomuto, když by chtěl získat
aktuální informace o uživateli:

public function renderDefault()
{
    $user = $this-&amp;gt;em-&amp;gt;getRepository(User:class)-&amp;gt;find($this-&amp;gt;getUser()-&amp;gt;getId());
    ...
}

A raději by psal pouze:

public function renderDefault()
{
    $user = $this-&amp;gt;getUser()-&amp;gt;getIdentity();
    ...
}

Nemluvě pak o použití v šablonách, kde je velice pohodlné přes
{$user-&amp;gt;getIdentity()} přistupovat k čerstvé entitě, než
si někde v beforeRender() metodě do šablony globálně vkládat
čerstvou entitu přihlášeného uživatele voláním repozitáře při
každém requestu.

Vlastní UserStorage

K tomu mít vždy aktuální identitu uživatele vám může právě pomoci
vlastní UserStorage. A jelikož nechci psát něco, co už bylo
někdy vymyšleno, proto bych rád ukázal jeden super balíček
majkl578/nette-identity-doctrine (odkaz na Github),
který je tak jednoduchý a přitom šikovný, že nemá smysl vymýšlet
vlastní kolo.

Balíček do vaší Nette aplikace nainstalujete jednoduše přidáním přes
Composer a vložením rozšíření do config.neon:

extensions:
    doctrine2identity: Majkl578\NetteAddons\Doctrine2Identity\DI\IdentityExtension

Základní idea je jednoduchá. Doctrine entita reprezentující vašeho
uživatele musí implementovat interface IIdentity, což jsme si
ukázali už výše. Při autentizaci tedy váš autentifikátor vráti pouze
entitu z repozitáře.

V getIdentity() pak vždy dostaneme aktuální entitu díky
vlastnímu UserStorage, které je implementováno ve zmíněném
balíčku. Navíc zde existuje podpora pro lazy načítání, takže samotná
databáze se pro načtení aktuální entity zavolá právě tehdy, až budete
z identity něco potřebovat. Ne dříve, tak jako by to bylo v případě
naivního přístupu přes beforeRender().

Mrkněme se na samotnou implementaci UserStorage
v balíčku, jak vlastně vypadá. Navíc to může být dobrá nápověda
pro to vidět, jak si tvořit vlastní UserStorage a to i v podobě
balíčků, pokud někdo nepreferuje Doctrine a rád by použil jiný
přístup.

V implementaci můžeme vidět, že vlastní UserStorage dědí od
výchozího a v konstruktoru navíc přidává závislost pro
EntityManager, skrze který pak budeme získávat aktuální entitu.
Dále se zde implementují dvě metody a to setIdentity(…), ke
kterému dochází typicky při vrácení entity z autentifikátoru a pak
samozřejmě getIdentity().

V setIdentity() dochází k šikovnému uložení entity konverzí
na FakeIdentity
ve výchozím UserStorage. De facto se (typicky při autentizaci) do
FakeIdentity poznamená identifikátor naší Doctrine entity a
o jakou třídu se jedná.

Při volání getIdentity() se pak z výchozího
UserStorage získá FakeIdentity a z ní pomoci informace
o identifikátoru a příslušné třídě se vytáhne aktuální entita
voláním instance EntityManager.

A to je celá věda. Jednoduché, přímočaré, efektivní. Osobně
používám balíček na několika projektech a funguje výborně.

Upozornění

V případě, že chcete změnit systém práce s identitami u již
existující aplikace, počítejte s tím, že starší sessions uživatelů
mohou při deserializaci vracet také starší výstup. Pokud tedy najednou
Identity změníte na entitu User implementující IIdentity, tak
v případě, že budete přistupovat k metodám, které nejsou dána
rozhraním IIdentity (třeba budete volat nad entitou
getFirstName()), může se klidně stát, že u identit
serializovaných před aktualizací dostanete něco, co jste nemuseli zrovna
očekávat, tj. starou identitu, která není vůbec vaší entitou
z Doctrine.

To lze vyřešit tak, že buď budete předpokládat a vyřešíte si, že
můžete získávat odlišné instance IIdentity odlišných tříd
nebo smažete uložené sessions tak, aby se všichni uživatelé museli
přihlásit znovu a tím jste všem od této chvíle při autentizaci ukládali
do sessions správnou identitu.
</description>
            <category></category>
            <category>php</category>
            <category>development</category>
            <category>nette</category>
        </item>
		<item>
            <title>PHPStorm - barevná schémata a tipy na závěr</title>
            <link>https://petrjirasek.cz/blog/phpstorm-barevna-schemata-a-tipy-na-zaver</link>
            <guid>https://petrjirasek.cz/blog/phpstorm-barevna-schemata-a-tipy-na-zaver</guid>
            <comments>https://petrjirasek.cz/blog/phpstorm-barevna-schemata-a-tipy-na-zaver#disqus_thread</comments>
            <description>
PHPStorm byl v jedné z anket vyhlášen jako jedno z nejlepších IDE pro
PHP. Já osobně ho používám už pár let a nemůžu si ho vynachválit. Taky
mě nepřestává překvapovat, co nového se dá v tomto studiu naučit.

Jelikož věřím, že řada jiných vývojářů používá také PHPStorm,
tak jsem si řekl, že se podělím o některé odkazy, které vám mohou
pomoci si práci v IDE zase o něco více zpříjemnit.

Barevná schémata

Barvičky, barvičky a zase barvičky. Kdo je konzervativní, používá
výchozí nastavení PHPStormu, tj. buď světlou defaultní verzi prostředí
nebo tmavou. Tuším, že ta tmavá se jmenuje Darcula. Já osobně
používal tu světlou, protože ta tmavá vypadala nehezky na mém starém
monitoru. Někdo také upřednostňuje tu tmavší, protože panuje názor, že
je příjemnější na oči, pokud do monitoru dlouho koukáte. Někdo ji zase
nemá rád, protože pak prý “dostane facku”, když přepne do jiné
aplikace. Ať už tak či onak, výběr je stejně subjektivní.

Já si pořídil nedávno nový monitor a řekl si, že zkusím opět tu
tmavou. Ale pořád jsem nebyl úplně spokojený. A tak jsem začal hledat a
našel dva zajímavé odkazy, kde si můžete stáhnout i další barevná
schémata pro váš PHPStorm. Na obou webech jsou vždy i náhledy, jak schéma
reálně vypadá a věřím, že si zde každý vybere. Navíc si u schématu
v PHPStormu můžete navíc dost parádně upravit detaily, které vám budou
vadit. Mi třeba přišla zelená barva u jednoho schématu příliš tmavá,
tak jsem si jednoduše udělal kopii schématu a v editoru barvičku
zesvětlil. A teď jsem spokojený.

Tady jsou ty odkazy na schémata:


	https://github.com/…lour-schemes
	(náhledy zde)

	http://www.phpstorm-themes.com/


Mi osobně se libí nejvíce z prvního odkazu téma
Peacock. Upravil jsem si v něm pak jen pár barviček, aby
byly vyráznější a zvětšil písmo a řádkování.

Schéma nainstalujete tak, že konfigurační soubor schématu vložíte do
příslušné složky tam, kde máte nastavení studia:

Příklad pro Windows 10:
C:\Users\-vase-uzivatelske-jmeno-\.WebIde100\con­fig\colors

U Macu nebo Linuxu to bude nejspíš podobné, jen ten konečný adresáč
bude trochu někde jinde.

Na závěr ještě dva tipy

Ještě bych na závěr hodil rád dva odkazy do publika. Oba se týkají
toho, jak efektivně pracovat s tímto IDE. Pokud jste nezaregistrovali, Jan
Tvrdík měl VÝBORNOU ukázku efektivní práce
s PHPStormem na PoSobotě. Určitě mrkněte, věřím, že se naučíte
zase něco nového:

Další super materiál z tohoto soudku je z Laracasts a jmenuje se Be Awesome In
PHPStorm. Velmi doporučuji.

Ať vám studio dobře slouží!
</description>
            <category></category>
            <category>php</category>
            <category>productivity</category>
            <category>phpstorm</category>
        </item>
		<item>
            <title>Dodržujte coding standard a zkuste code sniffer</title>
            <link>https://petrjirasek.cz/blog/dodrzujte-coding-standard-a-zkuste-code-sniffer</link>
            <guid>https://petrjirasek.cz/blog/dodrzujte-coding-standard-a-zkuste-code-sniffer</guid>
            <comments>https://petrjirasek.cz/blog/dodrzujte-coding-standard-a-zkuste-code-sniffer#disqus_thread</comments>
            <description>
Čistota a čitelnost kódu je důležitým aspektem každého projektu a
přispívá k udržitelnosti celého projektu. Tak jako se různá zařízení
domlouvají skrze pevně dané protokoly, tak se i vývojáři často u open
source projektů, ale také privátních, domlouvají na coding standardu, aby
jimi produkovaný kód vypadal kód jako jednoho muže (nebo jedné
ženy ).

K tomu přispívá právě coding standard, který definuje pravidla pro
úpravu kódu. Upravuje formátování, jmenné konvence nebo jeho
strukturu apod.

Proč je dobré dodržovat coding standard?

Pokud máme určený coding standard, tak se zvyšuje čitelnost kódu
v celém projektu. Zvyšuje se tím zároveň čitelnost pro ostatní členy
v týmu, což pak může vést k vyšší produktivitě, jelikož se člověk
může zorientovat rychleji a nemusí ztrácet svou mentální energii a čas
kvůli neustále adaptaci v různě formátovaném kódu v rámci jednoho
projektu.

CodeSniffer

CodeSniffer je naopak nástroj, který umožňuje daný coding standard
v kódu kontrolovat. PHP_CodeSniffer
umožňuje kromě předdefinovaných standardů (PEAR, Zend, PSR-1, PSR-2 atd.)
používat i vlastní, takže pokud vám nějaký z nich nevyhovuje, klidně
můžete jít vlastní cestou.

Řada IDE CodeSniffer podporuje, třeba PhpStorm disponuje vcelku podrobným
návodem zde.
Po integraci nástroje je pak PhpStorm schopen zobrazovat varovné hlášky
v částech kódu, které nesplňují pravidla vybraného standardu.

Pokud si chcete nainstalovat CodeSniffer pomoci balíčku, můžete použít
squizlabs/php_co­desniffer(repo­zitář zde). Defaultně
nabízí standardy PEAR, PSR-1, PSR-2.

CodeSniffer jako nástroj je schopen také v některých případech
formátování kódu dle standardu upravit za vás, což může pomoci, pokud
třeba váš aktuální projekt podobu kódu nemá ustálenou a nechcete se
s tím dělat ručně.

Další standardy

Existuje řada dalších standardů jako je třeba Symfony
Coding Standard, který vychází z PSR-2 nebo třeba Nette Coding Standard. Pokud by
jste hledali pravidla pro CodeSniffer pro Symfony, tak mrkněte zde.

Pěkny standard má také Zenify/CodingStan­dard (zde) od Tomáše Votruby a
vychází ze standardu Nette. Dokonce umí také přidat pre-commit hook, který
vždy spustí CodeSniffer na modifikovaných souborech při commitu.

A co vy? Používáte CodeSniffer, máte vlastní standard nebo používáte
už připravené řešení?
</description>
            <category></category>
            <category>php</category>
            <category>development</category>
            <category>nette</category>
            <category>phpstorm</category>
        </item>
		<item>
            <title>Jak konzistentně ukládat entitu v DB a zároveň obrázek na disk</title>
            <link>https://petrjirasek.cz/blog/jak-konzistentne-ukladat-entitu-v-db-a-zaroven-obrazek-na-disk</link>
            <guid>https://petrjirasek.cz/blog/jak-konzistentne-ukladat-entitu-v-db-a-zaroven-obrazek-na-disk</guid>
            <comments>https://petrjirasek.cz/blog/jak-konzistentne-ukladat-entitu-v-db-a-zaroven-obrazek-na-disk#disqus_thread</comments>
            <description>
Dnes bych chtěl napsat zamyšlení nad tím, jak ukládat konzistentně
entitu do DB a zároveň obrázek nebo klidně i jiný soubor na disk. Aby to
bylo jasnější, popíšu nějaký příklad, s kterým se určitě
víceméně každý někdy setkal.

Představme si například situaci, kdy máme aplikaci, kde uživatel může
vkládat článek a k němu ilustrační obrázek. Když si představíme ten
proces, tak bude existovat pravděpodobně nějaký formulář, kde uživatel
zadá titulek a obsah článku a přiloží ilustrační fotografii, která se
bude zobrazovat ve výpisu článků. Po odeslání formuláře se provede
validace a pokud projde, tak inicializujeme entitu, které nastavíme titulek a
obsah. A teď zbývá vyřešit, jak uložíme fotografii. Fotografii nebudeme
ukládat do databáze, ale určitě na disk, proto si do entity poznačíme
název souboru, pod jakým bude fotografie na disku uložena.

A teď jsme právě na tom rozcestí. Jakým způsobem zajistíme, aby
pojmenování fotografie bylo unikátní a zároveň nedošlo k tomu, že při
uložení entity, která vyžaduje mít nastavený název souboru obrázku,
nedošlo k tomu, že obrázek na disku uložený nebude, ale entita
v databázi uložena bude a logicky tak dojde
k nekonzisten­tnímu stavu.

Nyní ukážu některá řešení, jak jsem je dříve někde viděl
použité a zmíním jejich nedostatky. Na závěr pak ukážu řešení, které
se zdá být nejvíce bezpečné.

Prvně uložit entitu, potom obrázek

Tahle strategie je asi nejméně bezpečná. Spočívá v tom, že prvně
uložíme entitu a po jejím uložení získáme ID entity. To poté použijeme
k tomu, abychom pod ním uložili v adresáři daný obrázek a po uložení
obrázku provedeme update entity tak, že nastavíme cestu k obrázku.

Nevýhoda je jasná, když dojde k selhání uložení obrázku, tak entita
bude sice uložena v databázi, ale obrázek k ní bude chybět. Tak samo,
pokud dojde k selhání aktualizace entity, budeme mít stejný problém. Na
druhou stranu, když se vše povede, budeme mít unikátní název obrázku.

Prvně uložit obrázek, potom entitu

U této strategie naopak budeme ukládat obrázek dříve, než entitu,
takže pokud se nepodaří uložit obrázek a dojde k problému, neuložíme
ani entitu. Na druhou stranu, je zde nutné vyřešit problém s unikátním
pojmenováním obrázku. Můžeme použít různé strategie:


	pomoci hashovací funkce získáme hash, ale ten sám o sobě nezaručuje,
	že nemůže existovat kolize (tj. že dva různé obrázky budou mít
	stejný hash)

	k hashovací funkci můžeme přiřetězit časovou značku, která šanci
	kolize ještě výrazněji sníží

	pro jistotu se můžeme podívat ještě na disk, zda obrázek s daným
	jménem neexistuje a pokusit se tak ještě více vyhnout kolizi


Suma sumárum, ve všech případech dostaneme prakticky unikátní
pojmenování. Obrázek uložíme pod “unikátním” označením a označení
si uložíme do entity. Entita se uloží a s vysokou pravděpodobností bude
konzistentní.

Nevýhodou v tomto případě může být, že pokud dojde k chybě při
uložení entity, tak nám na disku může zůstat obrázek, ke kterému nevede
reference. To je ale možné vyřešit nějakým skriptem, který by pravidelně
prošel disk a obrázky nemající žádnou referenci, by odstranil.

Druhá nevýhoda může být přehlednost. Ne vždy je úplně pohodlný
nemít v pojmenování obrázku systém a systém často tvoří právě ID
entity, případně v kombinaci s dalšími identifikátory, jako je třeba
EAN apod. Upřímně, mi osobně se nechce procházet složky s obrázky
obsahující soubory pojmenované nic neříkajícími značkami.

Uložíme obrázek do pracovní složky, uložíme entitu, přesuneme
obrázek, aktualizujeme en­titu

Nyní si představíme strategii, která se jeví jako nejbezpečnější.
U této strategie postupujeme tak, že daný obrázek uložíme dočasně do
nějaké pracovní složky. Poté spustíme transakci nad databází (BEGIN),
abychom zajistili atomičnost celé operace. Samotné uložení obrázku do
transakce nezahrnujeme, protože chceme, aby transakce byla co nejkratší.
Poté uložíme entitu a získáme její ID. Provedeme přesun a přejmenování
obrázku do regulérního adresáře a aktualizujeme entitu (pokud bychom
nechtěli ukládat název obrázku, který má odvozený formát z ID entity
k entitě, pak aktualizaci nemusíme provádět). Na závěr pak provedeme
potvrzení databázové transakce (COMMIT). Pokud během transakce dojde
k jakémukoliv problému, můžeme vyhodit výjimku a vrátit změny
(ROLLBACK).

U této strategie máme jak vždy konzistentní entitu, tak i konzistentní
adresář, kam obrázky ukládáme. Pojmenování obrázku je zároveň vždy
logické a může odpovídat našemu systému dle libosti.

Shrnutí

Strategie, které jsem zde zmínil spíš slouží k zamyšlení, než že
bych chtěl hrdě prohlašovat něco ve smyslu, že pouze ta či ona strategie
je jediná správná. Volba strategie často závisí na konkrétní aplikaci a
na tom, jak je daná funkcionalita kritická a taky, o jakém množství
obrázků (obecně souborů) bychom se bavili. Kupříkladu je rozdíl, pokud
eshop pracuje s desítkami tisíc souborů, které často načítá
v dávkách a když si někdo na blogu sem tam vloží příspěvek
s ilustrační fotografií.

Každopádně ocením jakýkoliv komentář k tomu, jakou vaši strategii
používáte při vašem use casu zrovna vy.

Na závěr přidám ještě odkaz na zajímavé rozšíření
k Doctrine, které pomáhá s persistencí souborů u entit.
</description>
            <category></category>
            <category>php</category>
            <category>development</category>
        </item>
		<item>
            <title>Tip na levné SSL certifikáty</title>
            <link>https://petrjirasek.cz/blog/tip-na-levne-ssl-certifikaty</link>
            <guid>https://petrjirasek.cz/blog/tip-na-levne-ssl-certifikaty</guid>
            <comments>https://petrjirasek.cz/blog/tip-na-levne-ssl-certifikaty#disqus_thread</comments>
            <description>
Před pár dny jsem řešil situaci, kdy jsme potřebovali dostat jeden
projekt pod HTTPS a hlavním faktorem pro rozhodnutí se stala cena, což asi
mnoho lidí nepřekvapí.

Ze zkušenosti z jiného projektu vím, že tam jsme za nějaký
“základní” certifikát platili kolem 700 – 800 Kč/rok. Jelikož jsem
ale nedávno zaregistroval na Twitteru zmínku o levných certifikátech s cenou 99 Kč/rok,
rozhodl jsem se, že to vyzkouším, i když jsem k tomu byl prvně dost
skeptický.

Rozhodl jsem se test udělat prvně na svém blogu, jelikož ta investice
nebyla příliš vysoká. Koupil jsem tedy certifikát na tři roky, aby cena
byla co nejnižší. Objednávka byla vcelku jednoduchá, kdy obdržíte
privátní klíč, zaplatíte a pak dále dostanete další podklady od
certifikační autority.

Certifikáty vydává Comodo, přičemž kompatibilita s prohlížeči je
udájně 99,3 %. Certifikát si můžete také pořídit na zkoušku na dobu
90 dní a je možné jej použít i pro komerční weby.

Po obdržení certifikátu jsem se poté domluvil se svým poskytovatelem
VPS, aby mi ho nainstaloval. Neměl s tím problém (mimochodem mám VPS od
Českého hostingu) a po nasazení a malé úpravě v routeru aplikace vše
fungovalo, jak mělo.

Ze zvědavosti jsem mrkl na web ve Firefoxu a Chrome a oba prohlížeče
považovali certifikát za důvěryhodný a já měl dobrý pocit z dobrého
nákupu. Bohužel jsem pak ale narazil na to, že u androidího mobilu pří
přístupu přes prohlížeč v mobilu nebyl certifikát považován za
důvěryhodný, což byl trochu problém a můj dobrý pocit byl rázem
zmírněn.

Ale jak se ukázalo, problém nebyl v certifikátu. Po trošce googlování
jsem narazil na diskusní vlákno,
které se zabývalo tím, že důvěryhodný certifikát na PC se netváří
být důvěryhodný na androidu a ve vlákně popsané řešení zabralo.
Technická podpora provedla úpravu dle popsaného řešení a od té doby
s androidem už není žádný problém.

Takže za mě velká spokojenost. Za pár korun mi teď web jede pod SSL a
pokud zrovna nemáte vyšší požadavky na váš certifikát, tak vám možná
tento tip ušetří trochu peněz.

Zde ještě přikládám SSL
report webu, kde lze mimo jiné vidět detailní informace
o certifikátu.

Aktualizace: Na základě komentářů z Twitteru a diskuse
by měla být brzo k dispozici alternativa zdarma https://letsencrypt.org/
</description>
            <category></category>
            <category>tutorial</category>
        </item>
		<item>
            <title>Jak přeložit v routě slug na entitu při volání v Nette?</title>
            <link>https://petrjirasek.cz/blog/jak-prelozit-v-route-slug-na-entitu-pri-volani-v-nette</link>
            <guid>https://petrjirasek.cz/blog/jak-prelozit-v-route-slug-na-entitu-pri-volani-v-nette</guid>
            <comments>https://petrjirasek.cz/blog/jak-prelozit-v-route-slug-na-entitu-pri-volani-v-nette#disqus_thread</comments>
            <description>
Dneska bych chtěl ukázat pro někoho vcelku zřejmou věc a to jak
přeložit v routě slug na entitu při volání v Nette. Abych to lépe
vysvětlil, ukážu jednoduchý příklad, oč mi jde.

Představme si, že máme třeba presenter PostPresenter a
v něm akci zobrazující detail příspěvku. V URL stránky máme pak
kupříkladu xyz.cz/blog/nejaky-slug, kde nejaky-slug odkazuje
na konkrétní entitu příspěvku. Akce detailu je zobrazena níže.

public function actionDetail($slug)
{
    if (!$post = $this-&amp;gt;postRepository-&amp;gt;findBySlug($slug)) {
        $this-&amp;gt;error(&amp;quot;Příspěvek nebyl nalezen.&amp;quot;);
    }
    ...
}

Doposud se u některých starších aplikací setkávám s tímto
přístupem, kdy se vlastně akci předá konkrétní slug z volání a na
základě něho je snaha najít konkrétní entitu. Principiálně to není
zrovna čisté řešení a navíc to vyžaduje, aby jsme pak v odkazech
předávali slug entity. Tzn. měli v šabloně něco takového.

&amp;lt;a n:href=&amp;quot;Post:detail $post-&amp;gt;slug&amp;quot;&amp;gt;{$post-&amp;gt;name}&amp;lt;/a&amp;gt;

Vidíme, že to není zrovna ideální a asi by bylo pěkné, kdybychom mohli
mít pouze toto:

&amp;lt;a n:href=&amp;quot;Post:detail $post&amp;quot;&amp;gt;{$post-&amp;gt;name}&amp;lt;/a&amp;gt;

A v akci presenteru předávali entitu přímo v argumentu a nemuseli
neustále duplikovat vícemeně obdobný kód v každé podobné akci detailu
pro různé entity a jiné akce:

public function actionDetail(Post $post)
{
    ...
}

Filtry v Nette

V Nette můžeme použít při vytváření rout filtry, které nám právě
mohou celou věc zjednodušit a umožnit používat zmíněné řešení. Prvně
se ale podívejme na výslednou routu:

...
    $router[] = new Route(&#039;blog/&amp;lt;post&amp;gt;&#039;, array(
        &#039;presenter&#039; =&amp;gt; &#039;Front:Post&#039;,
        &#039;action&#039; =&amp;gt; &#039;detail&#039;,
        &#039;post&#039;  =&amp;gt;  array(
            Route::FILTER_IN =&amp;gt; function ($post) {
                return $this-&amp;gt;postRepository-&amp;gt;findBySlug($post);
            },
            Route::FILTER_OUT =&amp;gt; function ($post) {
                return $post-&amp;gt;slug;
            }
        ),
    ));
...

V routě můžeme vidět, že aplikujeme vstupní filtr
FILTER_IN a výstupní filtr FILTER_OUT nad parametrem
post s tím, že routa směřuje na
Front:Post:de­tail. Vstupnímu a výstupnímu filtru je
přiřazena anonymní funkce, kde u vstupního filtru je předána hodnota
parametru post z URL adresy a výstupnímu filtru je naopak
předána entita, kterou můžeme vidět při generování linku
v šabloně.

Vstupní filtr tedy získá slug a voláním repozitáře získá entitu,
kterou vrátí a ta je pak předána akci v presenteru. Pokud by entita nebyla
vrácena, tak pak se pokračuje v kolekci rout na hledání shody s dalšími
routami.

Naopak výstupní filtr vrátí slug, který se má doplnit za parametr
post při generování adresy odkazu.

Toto řešení je dost přímočaré a efektivní. Pokud budete chtít něco
takového použít v kombinaci s nette/sandbox, tak jen upozorním, že metoda
createFactory
je zde statická, tzn. že nebudete moci přistupovat ke službám, které
budete požadovat v konstruktoru, tak jako můžete třeba vidět v příkladu
výše použití postRepository, která byla předána
v konstruktoru.

K tomu, aby to šlo, stačí odstranit klíčové slovo static
a provést malou úpravu configu pro incializaci routeru na:

# router
- petrjirasek\Routers\RouterFactory
router: @petrjirasek\Routers\RouterFactory::createRouter

V důsledku tato změna vede k tomu, že prvně inicializujeme router,
Nette mu předá závislosti v konstruktoru a poté nad instancí továrničky
routeru voláme metodu, která nám samotný router vrátí.

Param konvertory v Symfony

Na závěr jen trochu odbočím a zmíním, že obdobné věcí umí třeba
i Symfony. Pokud by se chtěl někdo podívat na konvertory v Symfony blíže,
může mrknout třeba zde.
</description>
            <category></category>
            <category>tutorial</category>
            <category>php</category>
            <category>nette</category>
        </item>
		<item>
            <title>Jak napsat vlastní Latte filtr v Nette?</title>
            <link>https://petrjirasek.cz/blog/jak-napsat-vlastni-latte-filtr-v-nette</link>
            <guid>https://petrjirasek.cz/blog/jak-napsat-vlastni-latte-filtr-v-nette</guid>
            <comments>https://petrjirasek.cz/blog/jak-napsat-vlastni-latte-filtr-v-nette#disqus_thread</comments>
            <description>
Někdy je vhodné si v Nette nadefinovat vlastní filtr, když nám výchozí filtry
nestačí, i když se jedná o širokou nabídku rozmanitých funkcí,
které v základu skutečně stačí. Dnes bych chtěl ukázat, jak si takový
vlastní filtr, který zpravidla slouží k úpravě vstupu do nějaké
cílové podoby, napsat.

Filtry se registrují do šablony a vždy lze registrovat anonymní funkci
nebo callback. Níže následuje ukázka registrace jednoduchého filtru, který
vrátí absolutní hodnotu čísla. Filtr je zpravidla vhodné registrovat
v metodě beforeRender v BasePresenteru nebo
v nějaké továrničce šablony.

...
    protected function beforeRender()
    {
        $this-&amp;gt;template-&amp;gt;addFilter(&#039;abs&#039;, function ($number) {
            return abs($number);
        });
    }
...

Filtr pak použijeme tak, jak jsme zvyklí:

{$number|abs}

Myslím, že použití je dost přímočaré a můžeme pokročit.

Na základě komentáře z diskuse od Aurielle (díky) si dovolím ještě
doplnit jeho ukázku registrace filtru přímo v configu, která je také dost
šikovná:

services:
    nette.latteFactory:
        setup:
            - addFilter(abs, @petrjirasek\Latte\AbsFilter)

    - petrjirasek\Latte\AbsFilter

Oddělený filtr ve vlastní třídě

Osobně mám rád ve věcech pořádek, proto není od věci si filtry psát
odděleně a moci je pak snadněji přenášet. Jednoduchý filtr absolutní
hodnoty by tedy mohl odděleně vypadat takto:

&amp;lt;?php

namespace petrjirasek\Latte;

use Nette\Object;


class AbsFilter extends Object
{

    /**
     * @param int $number
     * @return int
     */
    public function __invoke($number)
    {
        return abs($number);
    }
}

Můžeme si všimnout, že byla použita magická metoda
__invoke, která nám umožní volat objekt jako funkci. Při
registraci pak stačí inicializovat třídu filtru.

use petrjirasek\Latte\AbsFilter;
...
    protected function beforeRender()
    {
        $this-&amp;gt;template-&amp;gt;addFilter(&#039;abs&#039;, new AbsFilter);
    }
...

Filtr se závislostmi

Někdy může filtr disponovat závislostmi a vyžadovat některé služby
v jeho konstruktoru. V praxi se může jednat o situaci, kdy nějaký filtr
potřebuje svůj výstup cachovat nebo používá službu, která daný vstup
převádí na požadovány výstup. Jelikož se asi nikomu nechce předávat
závislosti ručně při inicializaci, je vhodné použít automatické
dosazování závislosti v kombinaci s anotací @inject.
Řešení pak bude vypadat takto:

use petrjirasek\Latte\AbsFilter;
...
    /** @var AbsFilter @inject */
    public $absFilter;
...
    protected function beforeRender()
    {
        $this-&amp;gt;template-&amp;gt;addFilter(&#039;abs&#039;, $this-&amp;gt;absFilter);
    }
...

Také je nezbytné, aby filtr byl zaregistrován jako služba
v config.neon.

services:
    - petrjirasek\Latte\AbsFilter

Při tomto řešení jsou závislosti automaticky při inicializaci předány
a filtr je v pořádku zaregistrován. Jen doplním, že AbsFilter
aktuálně žádnou závislost nemá, ale kdyby měl nebo kdyby jsme měli jiný
filtr se závislostmi, tak by byly skutečně předány.

Tipy na filtry

Pokud vás stále nenapadá, kdy by jste mohli vlastní Latte filtr uplatnit,
tak zkusím naznačit pár případu užití.

Jedním z typických příkladů je třeba konverze data do tvaru vydáno
před X hodinami, před 5 dny atd. Můžete si napsat tak
vlastní filtr, jehož vstupem bude instance DateTime označující
například datum vydání článku a filtr při aplikací vrátí relativní
vyjádření času k aktuálnímu okamžiku.

Dalším příkladem může být použití Texy. Buď dost nehezky předáte
do šablony instanci Texy, zpracujete text a ještě to obalíte makrem pro
cachování nebo můžete hezky vše zapouzdřit do Latte filtru, kde jednou ze
závislostí bude jak Texy, tak nějaká cache, kam budete výstup
pravděpodobně ukládat.

A do třetice mě napadá případ, kdy máte vícejazyčný web a datumy
v nějakém vámi definovaném výchozím českém formátu. Pro každý jazyk
ale může být výchozí podoba formátu různá a tak se nabízí situace, kdy
filtru předáte informaci o jazyku a filtr pak bude aplikovat formát data dle
daného jazyka.

A jak pracujete s filtry vy? Co se vám osvědčilo?
</description>
            <category></category>
            <category>tutorial</category>
            <category>php</category>
            <category>nette</category>
        </item>
		<item>
            <title>5 let studia na FI MUNI za mnou</title>
            <link>https://petrjirasek.cz/blog/5-let-studia-na-fi-muni-za-mnou</link>
            <guid>https://petrjirasek.cz/blog/5-let-studia-na-fi-muni-za-mnou</guid>
            <comments>https://petrjirasek.cz/blog/5-let-studia-na-fi-muni-za-mnou#disqus_thread</comments>
            <description>
Za chvíli to budou dva měsíce, co jsem úspěšně dokončil magisterské
studium na FI MUNI v oboru Umělá inteligence a zpracování přirozeného
jazyka. Obhájil jsem svou diplomovou práci zabývající se klasifikací
sentimentu a anomálními uživateli na sociální síti Twitter, za kterou jsem
obdržel Cenu děkana. To ale není to podstatné.

Musím se přiznat, že dokončením studia jsem nezískal pocit, že bych
teď konečně dosáhl vyšší šance na své uplatnění, ale spíš mi spadl
kámen ze srdce, že už celý ten cyklus studia nemusím absolvovat znovu a
hlavně, že ta investice času byla potvrzena formální tečkou na konci.
Když jsem se ohlédl zpět na mé dojmy po třech
letech studia na FI MUNI, tak abych byl upřímný, nic bych v tom článku
dnes pravděpodobně nezměnil.

Když jsem si vybíral po těch třech letech bakalářského studia, jakým
směrem budu dál pokračovat, Umělá inteligence a zpracování přirozeného
jazyka byla víceméně jasná volba, jelikož už jsem z bakalářského
studia měl základ, který jsem nechtěl jen tak zahodit. Navíc, nechtěl jsem
jít studovat třeba Aplikovanou informatiku, jelikož jsem měl pocit, že by
mě příliš neobohatila.

Absolvoval jsem rozhodně zajímavé předměty, které mě obohatily více,
než bych čekal. Myslím tím hlavně předměty zaměřené na strojové
učení a neuronové sítě, kde sice zpočátku se zdálo být vše náročné,
ale po prolomení základní bariéry se ukázalo, že je to opravdu zajímavá
oblast obrovských možností.

Na druhou stranu, byly také předměty, které jsem považoval za nutné zlo
nebo jsem si je zapsal jen proto, abych splnil podmínky studijního programu.
Zajímavým společným rysem těchto předmětů byla skutečnost, že se
zpravidla jednalo o předměty hromadné, které byly založeny na principu,
kdy jsou celý semestr pouze nepovinné přednášky a na konci závěrečná
písemná zkouška.

Preference různých studentů jsou vždy odlišné, takže je pochopitelné,
že každý si najde svůj okruh zájmu. Spíš je zajímavé, s jakou
motivací vlastně člověk studuje. Někdo jen proto, aby si splnil své a
večer mohl zajít na pivo. Někdo naopak proto, že se chce něco nového
dozvědět.

Já začal studium na vysoké škole vnímat více jako mentální cvičení
a rozšiřování obzorů než jako cílené studium. Jenom přece je rozdíl,
když se třeba PHP vývojář cíleně zaměřuje na zdokonalování se
v Doctrine nebo zajde na školení Nette, než když studuje, za jakých
podmínek je třída NP složitostních problému řešených v polynomiálním
čase na nedeterministickém Turingově stroji rovna třídě P.

Chci tím říct, že je strašně důležité si uvědomit, co od školy,
kterou studuji, očekávám. Já očekával po bakalářském studiu mentální
cvičení, rozšíření obzorů v nových oblastech a informatice jako takové
se zaměřením se na umělou inteligenci. Naopak jsem nečekal, že mi studium
pomůže v mých dovednostech dělat vývoj webových aplikací. Na druhou
stranu, tím, že jsem objevil témata jako neuronové sítě, strojové učení
v kombinací s analýzou dat (analýza odlehlých bodů, asociační pravidla
apod.), tak jsem začal více přemýšlet nad tím, jak to vlastně více
propojit s tím, co dělám. A ač se to nemusí na první pohled zdát, tyto
témata jsou aktuální i na webu a jejich popularita rozhodně ještě
poroste.

A to je vlastně to, co jsem chtěl říct. Člověk by měl být
střízlivý ve svých očekáváních. Škola vám může otevřít nové
obzory, ale pokud se nebudete chtít učit vy a to nejen ve škole, škola vás
to nenaučí. FI MUNI vám právě ty nové obzory otevřít může. Je to
dobrá škola, ne vše je sice ideální, ale to spíš souvisí než se školou
samotnou s vysokoškolským systémem jako takovým.
</description>
            <category>life</category>
            <category>study</category>
            <category></category>
        </item>
    </channel>
</rss>