<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Programování a vývoj &#8211; Medio Blog</title>
	<atom:link href="https://blog.medio.cz/rubriky/programovani-a-vyvoj/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.medio.cz</link>
	<description></description>
	<lastBuildDate>Sun, 17 Apr 2016 14:54:09 +0000</lastBuildDate>
	<language>cs</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.1</generator>
	<item>
		<title>Genesis: Efektivní build PHP aplikací</title>
		<link>https://blog.medio.cz/genesis</link>
		
		<dc:creator><![CDATA[Adam Bísek]]></dc:creator>
		<pubDate>Mon, 18 Apr 2016 07:00:28 +0000</pubDate>
				<category><![CDATA[Programování a vývoj]]></category>
		<category><![CDATA[build]]></category>
		<category><![CDATA[deploy]]></category>
		<category><![CDATA[genesis]]></category>
		<category><![CDATA[phing]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.medio.cz/?p=3945</guid>

					<description><![CDATA[Dlouhá léta jsem hledal efektivní cestu, jak buildovat složitější aplikace. V PHP byl dlouho hegemonem Phing. Jde o nástroj, který svými možnostmi ustrnul hluboko v minulé dekádě. Nezbylo, než si napsat vlastní tool: Genesis.]]></description>
										<content:encoded><![CDATA[<p><span style="font-weight: 400">Dlouhá léta jsem hledal efektivní cestu, jak buildovat složitější aplikace. Na straně Javascriptu se mi před časem zalíbil Gulp, který klade kód nad konfiguraci, což je flexibilnější a přehlednější, než konfigurace, kterou vyznává například Grunt.</span></p>
<p><span style="font-weight: 400">V PHP byl dlouho hegemonem <a href="https://www.phing.info/">Phing</a>. Ten mi ovšem z mnoha důvodů nikdy nevyhovoval, i proto, že jde o nástroj, který svými možnostmi ustrnul hluboko v minulé dekádě. Například konfigurace v XML je </span><a href="http://www.imagehosting.cz/images/skitchj.jpg"><span style="font-weight: 400">čirý masochismus</span></a><span style="font-weight: 400">. A zápisy běžných jazykových statementů jako if, try-catch, apod., do jakéhokoliv dokumentu, nebudou nikdy tak efektivní, jako vyjádření přímo v kódu. Vyzkoušel jsem i pár dalších nástrojů, například <a href="http://taskphp.github.io/">TaskPHP</a>, ale nepokrývaly mé potřeby.</span></p>
<p><span style="font-weight: 400">Pohrával jsem i s myšlenkou, že bych pro build PHP použil Gulp, nebo něco z ekosystému Javascriptu, ale v okamžiku, kdy build potřebuje použivat struktury v PHP, tak by to nefungovalo. Nezbylo, než si napsat vlastní tool: Genesis.</span></p>
<h2><span style="font-weight: 400">Genesis</span></h2>
<p><span style="font-weight: 400"><a href="https://github.com/genesis-php/genesis">Genesis</a> se skládá jen z několika tříd a snaží se stát na jednoduchosti &#8211; build je vyjádřen přímo (viz níže)</span><span style="font-weight: 400">. Jde o třídu, která má libovolné množství tasků = public metod runXyz(). Což přináší výhodu plného využití objektového návrhu &#8211; dědičnost, kompozici, </span><span style="font-weight: 400">apod. Jaká třída se pro build použije si zvolíte v konfiguraci, pouze musí implementovat jednoduché rozhraní Genesis\IBuild.</span></p><pre class="crayon-plain-tag">class Build extends Genesis\Build
{

   public function runInit()     
   {
        // commands here
   }

}</pre><p><span style="font-weight: 400">Samotný příkaz v CLI je jednoduchý:</span></p><pre class="crayon-plain-tag">genesis &lt;nazev-tasku&gt; &lt;volitelny-dalsi-argument&gt;</pre><p><span style="font-weight: 400">např. tedy:</span></p><pre class="crayon-plain-tag">genesis init dev</pre><p><span style="font-weight: 400">Zavolá v build třídě metodu runInit(), přičemž argumenty ‘init’ a ‘dev’ se předají také a je možné s nimi pracovat.</span></p>
<p><span style="font-weight: 400">Lze použít parametr ‘&#8211;working-dir’, který umožní spustit build v jiném, než aktuálním adresáři. V konfiguračním kontejneru je vždy dostupná proměnná ‘</span><span style="font-weight: 400">workingDirectory’, která je vždy naplněná cestou k aktuálnímu pracovnímu adresáři.</span></p>
<p><span style="font-weight: 400">Sestavování aplikace obnáší spoustu úkolů, kde je nejlepší využít proměnné, protože například seznam adresářů k vytvoření, nebo cestu k executable PHPUnitu nechceme mít natvrdo zadrátovanou v kódu. Pro uložení konfigurace se pro jeho jednoduchost používá formát neon, známý z Nette Frameworku, přičemž podporuje proměnné, podobně jako v Nette.</span></p>
<h2><span style="font-weight: 400">Ukázkový jednoduchý build</span></h2>
<p><span style="font-weight: 400">Build potřebuje vlastně jen dvě věci: třídu se samotným buildem a konfigurační soubor.</span></p>
<p><b>config.neon</b></p><pre class="crayon-plain-tag">class: Build # naše třída
publicDirectory: %workingDirectory%/../public # proměnná workingDirectory je vždy naplněná
directoriesToCreate:
  "%publicDirectory%/data": "0777"
  "%publicDirectory%/data/files": "0777"</pre><p><b>Build.php</b></p><pre class="crayon-plain-tag">class Build extends Genesis\Build
{

   public function runInit()
   {
        $this-&gt;logSection('Create directories and files.');
        foreach ($this-&gt;container-&gt;directoriesToCreate as $directory =&gt; $chmod) {
             $command = new Commands\Filesystem\Directory();
             $command-&gt;create($directory, $chmod);
        }
   }
}</pre><p><span style="font-weight: 400">Volitelně může být v pracovním adresáři soubor </span><b>bootstrap.php</b><span style="font-weight: 400">, který se dá použít k zajištění autoloadingu, integraci s vaší aplikací (o tom dále), nebo k čemukoliv jinému. Aktuální <a href="https://github.com/genesis-php/example">example na Githubu</a>.</span></p>
<h2><span style="font-weight: 400">Integrace s PHP aplikací a jejími službami</span></h2>
<p><span style="font-weight: 400">U složitějších aplikací je nezbytné, aby se buildovací proces dostal k některým službám z aplikace. Například v Nette Framework si v DI kontejneru vytvoříte databázové spojení, které bude build také potřebovat.</span></p><pre class="crayon-plain-tag">$container  = require_once __DIR__ . '/../app/bootstrap.php'; # načteme bootstrap frameworku

$configContainer = new Genesis\Config\Container();
$configContainer-&gt;databaseConnection = $container-&gt;databaseConnection;
return $configContainer;</pre><p><span style="font-weight: 400">Pokud bootstrap.php z pracovního adresáře buildu vrátí instanci třídy </span><span style="font-weight: 400">Genesis</span><span style="font-weight: 400">\</span><span style="font-weight: 400">Config</span><span style="font-weight: 400">\</span><span style="font-weight: 400">Container, </span><span style="font-weight: 400">tak se spojí s obsahem kontejneru generovaného ze souboru config.neon. </span></p>
<p><span style="font-weight: 400">Pokud vás nástroj zaujal, můžete si ho snadno vyzkoušet. Na Githubu je</span><a href="https://github.com/genesis-php/example"><span style="font-weight: 400"> example project</span></a><span style="font-weight: 400"> a vyzkoušení je otázka několika příkazů během pár sekund.</span></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Jak zobrazujeme obrázky</title>
		<link>https://blog.medio.cz/jak-zobrazujeme-obrazky</link>
					<comments>https://blog.medio.cz/jak-zobrazujeme-obrazky#comments</comments>
		
		<dc:creator><![CDATA[Matěj Humpál]]></dc:creator>
		<pubDate>Wed, 05 Nov 2014 07:00:02 +0000</pubDate>
				<category><![CDATA[Programování a vývoj]]></category>
		<category><![CDATA[Dekompozice]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.medio.cz/?p=2304</guid>

					<description><![CDATA[V předchozím článku jsme si ukázali, jak ukládáme soubory obrázků na úložiště a mapujeme je na entity. To bylo jednoduché. Mnohem zajímavější je, jak obrázky zobrazit v aplikaci navenek.]]></description>
										<content:encoded><![CDATA[<p>V <a href="https://blog.medio.cz/jak-ukladame-obrazky">předchozím článku</a> jsme si ukázali, jak v <a href="http://www.medio.cz/">Mediu</a> ukládáme soubory obrázků na úložiště a mapujeme je na entity. To bylo jednoduché. Mnohem zajímavější je, jak obrázky zobrazit v aplikaci navenek.</p>
<p>Těžko budete nutit klienta, aby každý obrázek před nahráním zmenšil a ořízl na velikost odpovídající zobrazení na webu. Už vůbec ne na všechny velikosti, které na webu používá. A co když přidáte další zobrazení? Nebo se změní stávající velikosti kvůli redesignu? Zmenšovat obrázky jen přes HTML je pak neuctivé k času načítání stránky a majitelům mobilních FUP. Jak elegantně to děláme u nás?</p>
<p>Malá lákací ukázka: zobrazení obrázku na <a href="https://www.ticketon.cz/">titulce jednoho našeho projektu</a> vypadá v kódu takto:</p><pre class="crayon-plain-tag">{$performance-&gt;getImage() |thumbnail:large |imagehtml, $performance-&gt;getDescription()}</pre><p>Ano, správně, je to zápis v šablonovacím jazyce <a href="http://latte.nette.org">Latte</a>. Getter <code>$performance-&gt;getImage()</code> vrací, tadá, instanci třídy <code>Image</code>, kterou jsme si popsali v <a href="https://blog.medio.cz/jak-ukladame-obrazky">předchozím článku</a>. Ta se předá šablonovacímu filtru (v dřívějších verzích Nette se filtrům říkalo helpery) <code>thumbnail</code>, spolu s klíčem, který obsahuje informaci o tom, jaké změny před zobrazením na obrázku použít. Ten vrátí cestu k souboru na disku a předá ji filtru <code>imagehtml</code>, který cestu zkrátí na URL, vytvoří HTML tag<code><img alt="" /></code>, cestu mu nastaví do atributu <code>src</code>, a popisek z <code>$performance-&gt;getDescription()</code> do atributu <code>alt</code>. Elegantněji už to snad ani nejde.</p>
<p>Rozeberme si to teď trochu podrobněji. Filtr <code>thumbnail</code> volá třídu <code>ThumbnailGenerator</code>. Ta krom instance <code>IFileRepository</code> a instance <code>IImageFileNamingStrategy</code> dostane pole klíčů jednotlivých velikostí obrázků používaných v dané aplikaci. Třeba z konfigurace.</p>
<div class="bulb"><code>ThumbnailGenerator</code> by měl obdržet sice instanci stejné třídy <code>FilesystemRepository</code>, ale s jiným <code>$rootDir</code>, než je nastaven v instanci pro originály obrázků. Originály chceme typicky ukládat někde mimo document root, zatímco zmenšeniny by měly být z internetu dostupné.</div>
<p></p><pre class="crayon-plain-tag">imageSizes:
	square:
		width: 150
		height: 150
		flag: crop
	large:
		width: 960
		height: 960
		flag: fit</pre><p>Atributy <code>width</code> a <code>height</code> jsou jasné, vlajky <code>flag</code> používáme tři: <code>fit</code> vsouká obrázek do daných rozměrů a nikdy jej nezvětšuje, <code>crop</code> jej na ně ořízne. Pofiderní vlajka <code>enlarge</code> funguje jako <code>fit</code>, ale vsouká obrázek na dané rozměry, i kdyby byl sebemenší.</p>
<p>Klíče nám slouží jen pro pohodlnější určení varianty zmenšení a předávání parametru v šabloně (viz poznámky). Klidně bychom mohli předávat přímo trojici argumentů, ale takto alespoň víme, co se všechno v aplikaci používá, máme to přehledně na jednom místě a bráníme divokým vývojářům v přílišném rozletu.</p>
<p>Pak se nám bude hodit metoda <code>getThumbnailName()</code>, o kterou obohatíme rozhraní <code>IImageFileNamingStrategy</code>, potažmo v našem konkrétním případě jeho implementaci <code>HashFileNamingStrategy</code>. Ta bude přes <code>ThumbnailGenerator</code> vracet jméno souboru zmenšeniny obrázku, podle cesty k originálu a zadaných parametrů. Parametry jsme podle klíče z šablony našli v konfiguraci.</p><pre class="crayon-plain-tag">public function getThumbnailKey($path, $width, $height, $flag)
{
	// rozparsujeme jméno původního souboru pomocnou metodou
	$infoFromOriginalPath = $this-&gt;getInfoFromOriginalPath($path);
	
	$imageId = $infoFromOriginalPath['id'];
	$md5 = $infoFromOriginalPath['hash'];

	$sizes = $width . 'x' . $height . '-' . $flag;

	$key = $md5
			. '-' . $imageId
			. '-' . $sizes
			. '.'
			. strtolower(pathinfo($path, PATHINFO_EXTENSION));
	return $key;
}</pre><p>Tím dostaneme z cesty k souboru, který jsme si uložili, a klíče <code>large</code> jméno souboru.</p><pre class="crayon-plain-tag">2f/25/2f2555d95ee03f950c9ddae2f1692c55-42-960x960-fit.jpg</pre><p>Ten předáme šabloně a ta ho (po úpravách filesystémové cesty na korektní URL s cestou k obrázkům) vypíše na výstup. A máme hotovo.</p>
<p>&#8230;cože? Že jsme nikde nevytvořili samotnou zmenšeninu obrázku? Správně, nevytvořili. Starat se o vytváření všech zmenšenin při uploadu je zbytečně náročné. Navíc některým nahraným obrázkům se nikdy nemusejí vytvořit všechny možné velikosti. A znovu &#8211; co když variantu přidáme? Museli bychom projít všechny originály a variantu jim vytvořit ad hoc.</p>
<p>Ne, vytvoření raději necháme na aplikaci spolu s chytrým rewritem a/nebo routou.</p>
<h2>Jak obrázkům vytváříme zmenšeniny</h2>
<p>Pokud už soubor s požadovaným obrázkem na disku existuje, vrátí ho webový server rovnou. Nespouští se vůbec žádné PHP s nějakým Nette a vším tím overheadem okolo. Prostě se ze serveru vydá statický soubor.</p>
<p>Co se ale stane, když se uživatel pokusí přistoupit ke zmenšenině obrázku, která ještě na serveru není? Využijeme teď toho, že se frameworku předávají všechny požadavky na přístup k cestám, které na serveru fyzicky neexistují.</p>
<p>Zásadní technikou zpracování požadavků frameworkem je přesměrování všech potřebných volání na jednotný vstupní bod – v případě Nette je to soubor <code>index.php</code>. Ten pak už požadavky zpracuje interně.</p><pre class="crayon-plain-tag">$this[] = new Route('/&lt;? [a-z0-9]{2}&gt;/&lt;? [a-z0-9]{2}&gt;/&lt;key (.*)&gt;', array(
	'module' =&gt; 'Front',
	'presenter' =&gt; 'ImageService',
	'action' =&gt; 'default',
));</pre><p>Toto je routa Nette Frameworku, která požadavky na neexistující zmenšeniny posílá speciálnímu presenteru <code>ImageServicePresenter</code>, který:</p>
<ul>
<li>metodou v <code>HashFileNamingStrategy</code> z parametru <code>$key</code> zrekonstruuje informace o zmenšenině,</li>
<li>zkontroluje, jestli je kombinace vlastností zmenšeniny platná, tedy dostupná v nastavení (určitě bychom nechtěli, aby nám nějaký vtipálek pustil generování všech možných kombinací velikostí a vlajek),</li>
<li>zkontroluje, jestli existuje entita, pro kterou má zmenšeninu vytvořit,</li>
<li>pro jistotu ověří existenci originálního souboru,</li>
<li>vytvoří zmenšeninu podle zadaných kritérií, <strong>uloží ji na disk</strong> a vrátí její data (aby je uživatel viděl přímo ve svém požadavku).</li>
</ul>
<p>Pokud jakákoliv z kontrol neprojde, vrací požadavek chybu <code>404</code>.</p>
<p>Při dalším pokusu o přístup k té samé zmenšenině už server vrátí rovnou její statickou variantu, protože soubor fyzicky existuje.</p>
<div class="bulb">Na začátku routy pro zjednodušení chybí cesta do adresáře s obrázky, samozřejmě nemáme 256 adresářů struktury hned v rootu webu.</div>
<div class="bulb">Ve výchozím nastavení serveru nginx se do PHP-FPM vůbec nepouštějí požadavky na soubory s příponami typickými pro statický obsah. To je při zprovozňování <code>ImageServicePresenteru</code> potřeba mít na paměti a tuto podmínku odstranit.</div>
<h2>Poznámky z jedné kapsy</h2>
<p>K výše zmíněným jménům souboru jsme došli přes několik iterací. Původně jsme měli název originálu jako čistý MD5 hash a jméno souboru zmenšeniny jako jiný MD5 hash. Což je sice zvenku možná krásně čisté, aplikace se o to postará, ale pro debugování je to peklo.</p>
<p>Proto jsme do jména originálu přidali ID souboru (poznámka o kolizích byl jen lehký trolling), abychom mohli lépe dohledat, ke které entitě soubor patří.</p>
<p>Soubory se i celkem jednoduše procházejí běžnými konzolovými nástroji jako find, takže není problém třeba promazat ty, jejichž parametry už nevyhovují nastavení projektu.</p>
<p>Jedna ze slepých cestiček také byla místo trojice výška/šířka/vlajka ukládat do jména souboru jen klíč, ale záhy jsme zjistili, že pak soubory budou vyhnívat, můžou vznikat nepotřebné duplicitní soubory, při změně parametrů klíče se musí původní soubory ručně promazat, jinak se nenavytváří znovu, a jiné.</p>
<p>Co může být problém je změna velikosti obrázku na nějaké hodně navštěvované stránce &#8211; server pak začne generovat víc zmenšenin než běžně a může ho to i zabít. Pak je na pořadu dne zmenšeniny navytvářet nějakým workerem bokem v době, kdy není zátěž na serveru taková.</p>
<p>Při práci se jmény souborů používáme všude absolutní cesty.</p>
<p>Samozřejmě se může stát, že getter nevrátí instanci <code>Image</code> &#8211; nemusí být pro <code>$performance</code> povinná. Proto vypadá šablona častěji nějak takto:</p><pre class="crayon-plain-tag">{if $performance-&gt;getImage()}
	{$performance-&gt;getImage() |thumbnail:large |imagehtml, $performance-&gt;getDescription()}
{else}
	{NULL |dummyimagehelper:large |imagehtml}
{/if}</pre><p><code>dummyimagehelper</code> vrací dummy obrázky připravené specificky pro náš projekt. Ať už jsou vytvořené pro jednotlivé varianty zmenšenin ručně, nebo ať už je necháváme generovat z nějakého předpřipraveného zdroje podobně, jako zmenšeniny skutečných originálů.</p>
<h2 style="color: #000000;">Související články</h2>
<ul style="color: #474747;">
<li>Matěj Humpál: <a href="https://blog.medio.cz/jak-ukladame-obrazky">Jak ukládáme obrázky</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.medio.cz/jak-zobrazujeme-obrazky/feed</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title>Jak ukládáme obrázky</title>
		<link>https://blog.medio.cz/jak-ukladame-obrazky</link>
					<comments>https://blog.medio.cz/jak-ukladame-obrazky#comments</comments>
		
		<dc:creator><![CDATA[Matěj Humpál]]></dc:creator>
		<pubDate>Wed, 29 Oct 2014 07:00:31 +0000</pubDate>
				<category><![CDATA[Programování a vývoj]]></category>
		<category><![CDATA[Dekompozice]]></category>
		<category><![CDATA[Dependency Injection]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.medio.cz/?p=2095</guid>

					<description><![CDATA[Mnoho webových projektů, od malých až po ty největší, potřebuje nějak pracovat s obrázky. Ty je třeba ukládat, zpracovávat a jednoduše zobrazovat. Pojďme se podívat na to, jak obrázky ukládáme u nás v Mediu. Později řeč přijde i na elegantntí zobrazování uživatelům.]]></description>
										<content:encoded><![CDATA[<p>Mnoho webových projektů, od malých až po ty největší, potřebuje nějak pracovat s obrázky. Ty je třeba ukládat, zpracovávat a jednoduše zobrazovat. Pojďme se podívat na to, jak obrázky ukládáme <a href="http://www.medio.cz/">u nás v Mediu</a>. Později řeč přijde i na elegantní zobrazování uživatelům.</p>
<p>Řekněme, že každý obrázek (abychom o nich někde měli přehled a mohli k nim mít i nějaká metadata) je zastoupen entitou <code>Image</code>. Ta nejjednodušší může vypadat třeba nějak takto (pro ještě větší zjednodušení vynechávám gettery, settery a další):</p><pre class="crayon-plain-tag">class Image
{
	private $id;
	private $filename;
	private $extension;
}</pre><p>Abychom vytvořili vztah mezi naší entitou uloženou v jednom úložišti (typicky v databázi) a samotným souborem obrázku uloženým v jiném úložišti (disk, cloud, co si představíte), vytvoříme si třídu <code>ImageRepository</code>. Takto může vypadat:</p><pre class="crayon-plain-tag">class ImageRepository
{
	/** @var IFileRepository */
	private $fileRepository;

	/** @var IImageFileNamingStrategy */
	private $fileNamingStrategy;
	
	public function save(Image $image, $data)
	{
		return $this-&gt;fileRepository-&gt;save(
			$this-&gt;fileNamingStrategy-&gt;getOriginalName($image),
			$data
		);
	}
}</pre><p>Podívejme se na dvě závislosti třídy. <code>$fileNamingStrategy</code> je z nich trochu záhadnější. Protože každá instance projektu (nebo, řekněme, klient) může mít jiné požadavky na pojmenování souborů, implementují si po svém rozhraní <code>IImageFileNamingStrategy</code> s metodou <code>getOriginalName</code>. Například takto:</p><pre class="crayon-plain-tag">class HashFileNamingStrategy implements IImageFileNamingStrategy
{
	public function getOriginalName(Image $image)
	{
		return md5($image-&gt;getId() . $image-&gt;getFilename())
			. '-' . $image-&gt;getId() 
			. '.' . $image-&gt;getExtension();
	}
}</pre><p>Nebojte se, nehashujeme hesla. MD5 je tady jako hashovací algoritmus <em>víceméně</em> v pohodě. Aby bylo všemu učiněno zadost a nedostali jsme nedejbože duplicitní jméno souboru pro jinou instanci <code>Image</code> kvůli kolizi v algoritmu (oči divoce protáčející smajlík), můžeme do nezahashované části jména souboru přidat ID entity.</p>
<p>Dostaneme tak jméno souboru, které vypadá třeba takhle:</p><pre class="crayon-plain-tag">2f2555d95ee03f950c9ddae2f1692c55-42.jpg</pre><p>Závislost <code>IFileRepository</code> je nasnadě – o úroveň nižší repozitář, který volá funkce samotného úložiště, čtení, ukládání, zjištění existence souboru – ten nepotřebuje vědět nic o tom, že pracuje s třídou <code>Image</code>, a zároveň odstiňuje <code>ImageRepository</code> od konkrétní implementace ukládání &#8211; jeho výměnou (u nás typicky změnou jednoho řádku v konfiguraci DI kontejneru) můžeme vyměnit celé úložiště souborů – třeba z lokálního filesystému na Amazon S3.</p>
<p>Řekněme, že budeme obrázky ukládat do filesystému. <code>FilesystemRepository</code> tedy bude mít jako property nějaký kořenový adresář <code>$rootDir</code> (který každá instance dostane nejlépe jako parametr konstruktoru z konfigurace DI kontejneru).</p>
<p>Soubory na disku je třeba podle něčeho organizovat. Tisíce souborů v jednom adresáři už můžou dát strojům pěkně zabrat &#8211; hodí se nám tedy, aby soubory byly nějak rozumně rozloženy. Tady přijde ke cti MD5 začátek jména souboru &#8211; <code>FilesystemRepository</code> každý soubor uloží do stuktury podle prvních dvou dvouznaků jeho jména. Máme vyzkoušeno, že se tak soubory do adresářové struktury rozloží pěkně rovnoměrně. 256×256 adresářů should be enough for everyone. Soubor zmíněný výše relativně k <code>$rootDir</code> se uloží takto:</p><pre class="crayon-plain-tag">2f/25/2f2555d95ee03f950c9ddae2f1692c55-42.jpg</pre><p>Podle jména souboru víme, jak ho hledat v adresářové struktuře (kdybychom se chtěli ručně podívat, kde je třeba případný problém) a na konci souboru máme ID jeho entity – to kdyby byl problém na straně modelu.</p>
<div class="&quot;bulb">Dokonce bychom klidně mohli md5 hash zkrátit, třeba na 8 znaků, to by mělo stačit (o kolize nám už, jak jste mohli postřehnout, až tolik nejde).</div>
<p>Metoda save <code>ImageRepository</code> tedy uloží data obrázku. Podle podobného klíče bude v <code>ImageRepository</code> fungovat i vyzvednutí obrázku: předáme instanci <code>Image</code> metodě <code>getPath</code> a dostaneme cestu k souboru. Metoda <code>load</code> pak může vracet zase obrázek jako data.</p>
<p>Originály souborů, v našem případě obrázků, máme uložené na disku, dokážeme podle entity najít cestu k nim a dále s nimi manipulovat. V <a href="https://blog.medio.cz/jak-zobrazujeme-obrazky">následujícím článku</a> se podíváme na mnohem zajímavější věc, a to jak s obrázky dále manipulujeme a posíláme je uživatelům.</p>
<h2 style="color: #000000;">Související články</h2>
<ul style="color: #474747;">
<li>Matěj Humpál: <a href="https://blog.medio.cz/jak-zobrazujeme-obrazky">Jak zobrazujeme obrázky</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.medio.cz/jak-ukladame-obrazky/feed</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
			</item>
		<item>
		<title>Nette Addons Hackaton</title>
		<link>https://blog.medio.cz/nette-addons-hackaton</link>
					<comments>https://blog.medio.cz/nette-addons-hackaton#comments</comments>
		
		<dc:creator><![CDATA[Jan Marek]]></dc:creator>
		<pubDate>Tue, 17 Apr 2012 13:31:36 +0000</pubDate>
				<category><![CDATA[Programování a vývoj]]></category>
		<category><![CDATA[Nette]]></category>
		<guid isPermaLink="false">https://blog.medio.cz/?p=506</guid>

					<description><![CDATA[V kancelářích Medio Interactive se o víkendu konal 14.–15. 4. 2012 konal "Nette Addons Hackaton":http://forum.nette.org/cs/10578-nette-brain-cloud-2-addons-hackathon-praha-14-15-4-2012. Jeho cílem bylo vytvořit novou a mnohem lepší verzi již delší dobu nevyhovující stránky s "doplňky Nette Frameworku":http://addons.nette.org/. 
]]></description>
										<content:encoded><![CDATA[<p>V kancelářích Medio Interactive se o víkendu 14.–15. 4. 2012 konal <a href="http://forum.nette.org/cs/10578-nette-brain-cloud-2-addons-hackathon-praha-14-15-4-2012">Nette Addons Hackaton</a>. Jeho cílem bylo vytvořit novou a mnohem lepší verzi již delší dobu nevyhovující stránky s <a href="http://addons.nette.org/">doplňky Nette Frameworku</a>.</p>
<p>Na půdě Media se v sobotu dopoledne sešla <a href="http://srazy.info/nettebraincloud/2213">hromada vynikajících programátorů</a>. Pro začátek bez jakékoliv určité představy o tom, co vlastně budou dělat. Kupodivu to příliš nevadilo, ukázalo se, že domluvit se umíme. Z hlediska uživatelského rozhraní se nám stal inspirací web s <a href="http://mootools.net/forge/">doplňky MooTools</a> a z funkčního repozitář PHP knihoven <a href="http://packagist.org/">Packagist</a>.</p>
<h2>Pohled do naší programátorské kuchyňky</h2>
<p>Jako pracovní nástroj jsme zvolili pochopitelně Nette Framework. Přestože každý z nás má nějaké osvědčené postupy, jak v něm pracovat, dohodli jsme se, že se budeme držet běžných doporučených cest známých z dokumentace. Spoustu z nás čekala první větší zkušenost s Nette\Database. Nepoužili jsme žádné přídavné doplňky. Přece se nebudeme spolu dohadovat, proč že se použil plugin tvůj a ne ten můj, který je přece lepší!</p>
<p>Zdrojové kódy jsme verzovali pochopitelně pomocí Gitu a naše práce byla průběžně k vidění na <a href="https://github.com/Vrtak-CZ/nette-addons">GitHubu</a>. Došlo nám i několik pull requestů. Doufejme, že jich ještě v budoucnu spousta dorazí.</p>
<p>Vývoj probíhal velice živelně. Commity přibývaly jeden za druhým, takže neocenitelným pomocníkem se nám stal příkaz <code>git pull --rebase</code>, který bylo nutné zavolat před každým pokusem o push. Rozdělení práce probíhalo velice demokraticky, ale kupodivu naprosto bezproblémově. Ukázalo se, že máme mezi sebou specialisty na GitHub API, na Composer, na Bootstrap, případně na všechno, takže jsme si nekonkurovali.</p>
<p>Kvůli velkému počtu změn v architektuře se příliš nedařilo psát testy (což je jinak v prostorách Media nemyslitelné). Druhý den, v neděli, se ale prokázalo, že na testech je těžké jen začít a pár unit a selenium testů v projektu přibylo.</p>
<p>Pro rychlé prototypování nám byl užitečný CSS framework Bootstrap. Jestli zůstane i ve finální verzi zatím nevíme. Záleží na tom, jestli se ho povede přemluvit, aby nevypadal jako Twitter.</p>
<p>Na novém webu účastníci začali pracovat od 11 hodin v sobotu. Cca v 11 hodin dorazil i David Grudl, jen bych taktně pomlčel o tom, podle kterého časového pásma si nařídil hodinky. S programováním se končilo až někdy v půl páté nad ránem. Pak se dal krátký spací rozchod a v neděli v 11 byl opět sraz a ještě se několik hodin zmáklo. Byť mému biorytmu to příliš nesedlo, tak jsem se na vlastní oči přesvědčil, že <a href="https://twitter.com/#!/HonzaMarek/status/191335895234904064">pověsti o nočním životě programátorů</a> se 100 % zakládají na pravdě.</p>
<h2>Výsledek</h2>
<div id="attachment_513" style="width: 628px" class="wp-caption aligncenter"><a href="https://blog.medio.cz/wp-content/uploads/66WYfb4bR-5vrYGfjq9j.png"><img fetchpriority="high" decoding="async" aria-describedby="caption-attachment-513" class="size-full wp-image-513" src="https://blog.medio.cz/wp-content/uploads/66WYfb4bR-5vrYGfjq9j.png" alt="Nette Addons!" width="618" height="329" srcset="https://blog.medio.cz/wp-content/uploads/66WYfb4bR-5vrYGfjq9j.png 618w, https://blog.medio.cz/wp-content/uploads/66WYfb4bR-5vrYGfjq9j-300x159.png 300w" sizes="(max-width: 618px) 100vw, 618px" /></a><p id="caption-attachment-513" class="wp-caption-text">Nette Addons!</p></div>
<p>Výsledkem je z mého pohledu reprezentativní aplikace, která umí přehledně zobrazovat doplňky, spravovat jejich verze a závislosti na dalších doplňcích. Nabízí perfektní provázání se současnou Nette komunitou &#8211; funguje přihlašování uživatelů registrovaných přes fórum, umožňuje hodnocení přihlášenými uživateli. Doplňky lze téměř bezpracně importovat z GitHubu, což je defacto standard pro hostování open source softwaru. Systém umí přednačíst název z názvu GitHub repozitáře, další metadata ze souboru composer.json pro PHP balíčkovací systém <a href="http://getcomposer.org/">Composer</a> a popis doplňku ze souboru readme.*. Z mého pohledu snad nejužitečnější funkcí je to, že nové Nette Addons bude poskytovat soubory s metadaty pro Composer, takže kterýkoliv doplněk Nette půjde tímto perfektním nástrojem snadno nainstalovat přes příkazovou řádku.</p>
<h2>Malé video představení</h2>
<p><iframe title="Hackathon 2012: Addons" width="500" height="281" src="https://www.youtube.com/embed/9omM_AuUhnU?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.medio.cz/nette-addons-hackaton/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Testování nejen v PHPUnitu</title>
		<link>https://blog.medio.cz/testovani-nejen-v-phpunitu</link>
					<comments>https://blog.medio.cz/testovani-nejen-v-phpunitu#comments</comments>
		
		<dc:creator><![CDATA[Daniel Milde]]></dc:creator>
		<pubDate>Tue, 03 Apr 2012 12:58:43 +0000</pubDate>
				<category><![CDATA[Programování a vývoj]]></category>
		<category><![CDATA[PHPUnit]]></category>
		<category><![CDATA[Selenium]]></category>
		<category><![CDATA[Testování]]></category>
		<category><![CDATA[Video]]></category>
		<guid isPermaLink="false">https://blog.medio.cz/?p=481</guid>

					<description><![CDATA[Na videu z březnové Poslední soboty představuje Dan Milde v rámci své přednášky možnosti testování PHP aplikací s pomocí jednotkových, integračních i Selenium testů. Zmiňuje důvody pro testování a ukazuje konkrétní praktické postupy. Pro demonstraci používá jednoduchou ukázkovou aplikaci.]]></description>
										<content:encoded><![CDATA[<p>Na videu z březnové <a href="http://www.posobota.cz/">Poslední soboty</a> představuje Dan Milde v rámci své přednášky možnosti testování PHP aplikací s pomocí jednotkových, integračních i Selenium testů. Zmiňuje důvody pro testování a ukazuje konkrétní praktické postupy.</p>
<p>Pro demonstraci používá <a href="https://github.com/Dundee/testing-showcase">jednoduchou aplikaci</a>. Ta je vedle samotného testování zajímavá ještě v jednom ohledu. Jedná se totiž o konkrétní implementaci obecné architektury, kterou nedávno popisoval Vašek Purchart ve svém článku <a href="http://zdrojak.root.cz/clanky/architektura-aplikace-nad-doctrine-2/">Architektura aplikace nad Doctrine 2</a>. Veškerá aplikační logika je přenesena z presenterů do fasády, do budoucna tak například umožňuje snadno přidat více různých rozhraní, jako je konzole nebo mobilní aplikace.</p>
<p><iframe width="420" height="315" src="http://www.youtube.com/embed/YJ3Qx_ci44M" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>
<p>Ukázková aplikace využívá čisté dependency injection, což mimo jiné usnadňuje právě testovatelnost. Ve své přednášce k tomu Dan poznamenává: „V Nette jsou jen tři místa, kde byste měli používat kontejner jako service locator. Je to bootstrap, presenter loader respektive presenter factory a testy. Nikde jinde byste napřímo s kontejnerem pracovat neměli.“</p>
<p>Pokud vás téma návrhu a testovatelnosti webových aplikací zaujalo, navštivte naše školení <a href="http://akademie.medio.cz/vyvoj-webovych-aplikaci">Pokročilý vývoj a testování aplikací</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.medio.cz/testovani-nejen-v-phpunitu/feed</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Debugování s XDebug</title>
		<link>https://blog.medio.cz/debugovani-s-xdebug</link>
					<comments>https://blog.medio.cz/debugovani-s-xdebug#comments</comments>
		
		<dc:creator><![CDATA[Jindřich Samec]]></dc:creator>
		<pubDate>Wed, 03 Aug 2011 14:35:52 +0000</pubDate>
				<category><![CDATA[Programování a vývoj]]></category>
		<category><![CDATA[Debugování]]></category>
		<category><![CDATA[Interní školení]]></category>
		<category><![CDATA[Prezentace]]></category>
		<category><![CDATA[Profilování]]></category>
		<guid isPermaLink="false">https://blog.medio.cz/?p=218</guid>

					<description><![CDATA[Tématem našeho červencového interního školení bylo debugování a profilování PHP kódu s pomocí XDebug. Jindrova prezentace přináší základní záchytné body pro instalaci, konfiguraci i samotné používání tohoto nástroje.
]]></description>
										<content:encoded><![CDATA[<p>Tématem našeho červencového interního školení bylo debugování a profilování PHP kódu s pomocí <a href="http://xdebug.org/">XDebug</a>. Jindrova prezentace přináší základní záchytné body pro snadnou instalaci, konfiguraci i samotné používání tohoto nástroje.</p>
<p><object id="__sse8763321" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=debugovnsxdebug-110803092732-phpapp01&#038;stripped_title=debugovn-s-xdebug&#038;userName=mediocz" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse8763321" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=debugovnsxdebug-110803092732-phpapp01&#038;stripped_title=debugovn-s-xdebug&#038;userName=mediocz" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"/></object></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.medio.cz/debugovani-s-xdebug/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Mockování v PHPUnit</title>
		<link>https://blog.medio.cz/mockovani-v-phpunit</link>
					<comments>https://blog.medio.cz/mockovani-v-phpunit#comments</comments>
		
		<dc:creator><![CDATA[Václav Novotný]]></dc:creator>
		<pubDate>Mon, 14 Mar 2011 12:30:52 +0000</pubDate>
				<category><![CDATA[Programování a vývoj]]></category>
		<category><![CDATA[Interní školení]]></category>
		<category><![CDATA[Mock]]></category>
		<category><![CDATA[PHPUnit]]></category>
		<category><![CDATA[Prezentace]]></category>
		<category><![CDATA[TDD]]></category>
		<category><![CDATA[Testování]]></category>
		<guid isPermaLink="false">https://blog.medio.cz/?p=10</guid>

					<description><![CDATA[Při automatizovaném testování kódu své aplikace narazíte dříve či později na potřebu nahrazování některých skutečných tříd takzvanými mock objekty. Naše prezentace shrnuje, co vlastně mocky jsou, proč jsou potřeba a jak se s nimi pracuje v testovém frameworku PHPUnit.]]></description>
										<content:encoded><![CDATA[<p>Při <a href="stitky/testovani">automatizovaném testování</a> kódu své aplikace narazíte dříve či později na potřebu nahrazování některých skutečných tříd takzvanými <a href="/stitky/mock">mock objekty</a>. Naše prezentace shrnuje, co vlastně mocky jsou, proč jsou potřeba a jak se s nimi pracuje v testovém frameworku <a href="/stitky/phpunit">PHPUnit</a>.</p>
<p><span id="more-10"></span></p>
<div class="prezi-player">
<p><object id="prezi_uewbisokoszq" width="550" height="400" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="prezi_uewbisokoszq"><param name="movie" value="http://prezi.com/bin/preziloader.swf"/><param name="allowfullscreen" value="true"/><param name="allowscriptaccess" value="always"/><param name="bgcolor" value="#ffffff"/><param name="flashvars" value="prezi_id=uewbisokoszq&amp;lock_to_path=0&amp;color=ffffff&amp;autoplay=no&amp;autohide_ctrls=0"/><embed id="preziEmbed_uewbisokoszq" name="preziEmbed_uewbisokoszq" src="http://prezi.com/bin/preziloader.swf" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="550" height="400" bgcolor="#ffffff" flashvars="prezi_id=uewbisokoszq&amp;lock_to_path=0&amp;color=ffffff&amp;autoplay=no&amp;autohide_ctrls=0"/></object></p>
<div class="prezi-player-links">
<p><a title="" href="http://prezi.com/uewbisokoszq/mockovani-v-phpunit-35/">Mockování v PHPUnit 3.5</a> on <a href="http://prezi.com">Prezi</a></p>
</div>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.medio.cz/mockovani-v-phpunit/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Testování presenterů v Nette</title>
		<link>https://blog.medio.cz/testovani-presenter</link>
		
		<dc:creator><![CDATA[Ondřej Mirtes]]></dc:creator>
		<pubDate>Mon, 30 Aug 2010 12:29:00 +0000</pubDate>
				<category><![CDATA[Programování a vývoj]]></category>
		<category><![CDATA[Interní školení]]></category>
		<category><![CDATA[Nette]]></category>
		<category><![CDATA[PHPUnit]]></category>
		<category><![CDATA[Presenter]]></category>
		<category><![CDATA[Prezentace]]></category>
		<category><![CDATA[Testování]]></category>
		<guid isPermaLink="false">https://blog.medio.cz/?p=33</guid>

					<description><![CDATA[Při vývoji aplikací se snažíme všechen kód pokrývat automatizovanými testy. Podrobnějšími důvody pro testování se už v minulosti zabýval Ondra Mirtes v samostatném článku <a href="http://zdrojak.root.cz/clanky/testovani-neni-nastroj-ale-metoda-vyvoje/">Testování není nástroj, ale metoda vývoje</a>. Zvláštní kapitolou je přitom testování presenterů. Tomu jsme věnovali samostatné interní školení.]]></description>
										<content:encoded><![CDATA[<p>Při vývoji aplikací se snažíme všechen kód pokrývat automatizovanými testy. Podrobnějšími důvody pro testování se už v minulosti zabýval Ondra Mirtes v samostatném článku <a href="http://zdrojak.root.cz/clanky/testovani-neni-nastroj-ale-metoda-vyvoje/">Testování není nástroj, ale metoda vývoje</a>. Zvláštní kapitolou je přitom testování presenterů. Tomu jsme věnovali samostatné interní školení.</p>
<p><object id="__sse4855931" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=testovani-presenteru-100728080031-phpapp02&#038;stripped_title=testovn-presenter-v-nette" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse4855931" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=testovani-presenteru-100728080031-phpapp02&#038;stripped_title=testovn-presenter-v-nette" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object></p>
<p>Jaké máte vy zkušenosti s testováním presenterů/controllerů a obecně celého životního cyklu požadavku? Testujete i je, nebo se omezujete jen na testování dílčích tříd a izolovaných funkčností?</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Lehký úvod do PostgreSQL</title>
		<link>https://blog.medio.cz/lehky-uvod-do-postgresql</link>
					<comments>https://blog.medio.cz/lehky-uvod-do-postgresql#comments</comments>
		
		<dc:creator><![CDATA[Václav Novotný]]></dc:creator>
		<pubDate>Wed, 28 Jul 2010 10:23:07 +0000</pubDate>
				<category><![CDATA[Programování a vývoj]]></category>
		<category><![CDATA[Databáze]]></category>
		<category><![CDATA[Interní školení]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Prezentace]]></category>
		<guid isPermaLink="false">https://blog.medio.cz/?p=14</guid>

					<description><![CDATA[Chceme se s vámi dělit o zajímavé materiály z našich pravidelných vnitrofiremních školení. První na řadě je prezentace Vaška Novotného z jeho přednášky o PostgreSQL. Dá vám souhrnný přehled o všech důležitých vlastnostech a specifikách tohoto databázového systému, který v Medio Interactive při vývoji aplikací používáme.]]></description>
										<content:encoded><![CDATA[<p>Chceme se s vámi dělit o zajímavé materiály z našich pravidelných vnitrofiremních školení. První na řadě je prezentace Vaška Novotného z jeho přednášky o PostgreSQL. Dá vám souhrnný přehled o všech důležitých vlastnostech a specifikách tohoto databázového systému, který v Medio Interactive při vývoji aplikací používáme.</p>
<div class="prezi-player"><object id="prezi_57jewpnmnxnk" name="prezi_57jewpnmnxnk" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="600" height="400"><param name="movie" value="http://prezi.com/bin/preziloader.swf"/><param name="allowfullscreen" value="true"/><param name="allowscriptaccess" value="always"/><param name="bgcolor" value="#ffffff"/><param name="flashvars" value="prezi_id=57jewpnmnxnk&amp;lock_to_path=0&amp;color=ffffff&amp;autoplay=no&amp;autohide_ctrls=0"/><embed id="preziEmbed_57jewpnmnxnk" name="preziEmbed_57jewpnmnxnk" src="http://prezi.com/bin/preziloader.swf" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="600" height="400" bgcolor="#ffffff" flashvars="prezi_id=57jewpnmnxnk&amp;lock_to_path=0&amp;color=ffffff&amp;autoplay=no&amp;autohide_ctrls=0"></embed></object></p>
<div class="prezi-player-links">
<p><a title="Několik poznámek o PostgreSQL posbíraných od Pavla Stěhuleho a jinde." href="http://prezi.com/57jewpnmnxnk/postgresql/">PostgreSQL</a> on <a href="http://prezi.com">Prezi</a></p>
</div>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.medio.cz/lehky-uvod-do-postgresql/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
	</channel>
</rss>
