2018. szeptember 4., kedd

Kategória fa lekérdezése 3 szintig egy SQL utasításban

Optimalizálni kellett egy nagyobb kategória fának a lekérdezését, hogy egy SQL utasítással lehessen lekérdezni az összes szükséges információt, így ne legyen annyiszor megszólítva az adatbáziskezelő.

Természetesen már korábban is belefutottam a problémába, de eddig nem publikáltam semmit.

Tehát álljon itt egy SQL utasítás megjegyzésekkel:

--
-- SQL utasítás, amely faszerkezetbe rendezett kategóriákat tud
-- maximum 3 szintig listázni.
-- Az eredményt lineárisan bejárva a programozási nyelvben is lehet 
-- egy több dimenziós tömböt felépíteni, mert nem fog előfordulni,
-- hogy a felsőbb szint esetleg később jönne a listában!
--
SELECT

-- TODO: További oszlopokat szabadon hozzáadni!

-- Az első táblában lévő információkra van szükségünk:
c_1.id

-- Ebben az oszlopban a kategória hierarchiának megfelelően fordított
-- sorrendben kerülnek "," karakterrel összefűzésre
-- Az eredményhalmaz feldolgozása során nagy segítséget nyújthat egy többdimenziós
-- szerkezet összeállításához.
, CONCAT_WS(",", c_1.id, c_2.id, c_3.id) AS category_hierarchy

-- Ez az oszlop azt számolja ki, hogy hányadik kategória szinten 
-- fog elhelyezkedni az adott sorban lévő "c_1"-es kategória
, 1 + LENGTH(CONCAT_WS(",", c_1.id, c_2.id, c_3.id))
  - LENGTH( REPLACE ( CONCAT_WS(",", c_1.id, c_2.id, c_3.id), ",", "") )  AS level_number
-- A listázott kategóriához hozzácsatoljuk, ha lehetséges a szülő kategóriáját,
-- és a szülő kategóriához hozzácsatoljuk annak a szülőjét is, ha lehetséges:
FROM category_tree AS c_1
LEFT JOIN category_tree AS c_2 ON c_1.parent_id = c_2.id
LEFT JOIN category_tree AS c_3 ON c_2.parent_id = c_3.id
WHERE
-- Ez csak apróság, de általában mindenhol jelen van egy "Látható-e a kategória"
-- típusú oszlop információ, ami alapján csak a publikus kategóriák lesznek listázva
-- Természetesen láthatóság nem csak a levél kategóriára lesz ellenőrizve.
-- Ha a szülő nem látható, akkor nem lenne értelme listázni a benne lévő 
-- elemeket
(
c_1.status = 1
-- A 2. és 3. csatolt táblában az IS NULL is szükséges, mert nem biztos
-- hogy minden sorhoz lehetett további szülő és szülő-szülő kategóriát 
-- csatolni. Ekkor ugye a LEFT JOIN miatt ezek a mezők NULL értékúek lesznek.
AND (c_2.status IS NULL OR c_2.status = 1)
AND (c_3.status IS NULL OR c_3.status = 1)
)
-- Ez a feltétel azt köti ki, hogy az 1. vagy a 2. vagy 3. szintnek a gyökérben
-- kell végződnie, amelynek már nincs szülője
AND (
c_1.parent_id IS NULL
OR c_2.parent_id IS NULL
OR c_3.parent_id IS NULL
)
ORDER BY
-- Szükséges növekvő sorban a szint számnak megfelelő sorba rendezni az 
-- eredményeket, hogy az eredményhalmaz lineáris feldolgozása során ne legyen
-- olyan kategória, amelynek a szülője még nem került volna feldolgozásra.
level_number ASC
-- Érdemes valamilyen egyértelmű sorszámozást tartalmazó oszlop alapján 
-- még rendezni az eredményeket:
, c_1.sequence_number ASC





2018. augusztus 31., péntek

Laravel: cookie beállítása, hogy elérhető is legyen azonnal a response objektum megszületése előtt

Kóstolgatom a Laravel PHP keretrendszert.

Egyik problémám a címben némileg hosszan és hanyagul lett megfogalmazva.

Mert ugye van itt ez a:
Cookie::queue('key', 'value', 5);
Amivel a response létrejötte előtt lehet olyan cookie-kat definiálva, ami majd automatikusan hozzá lesz dobva a response objektumhoz, ha az már létezni tetszik. (nagyjából)

De nekem az kellett, hogy ha én azt mondom sablonosan, hogy
cookie_beallitasa($name, $value);
akkor a függvény meghívását követő sorban már ki tudjam adni a
\Cookie::get($name);
metódus hívást és éppen az előbb beállított $value értéket kapjam.

Erre jutottam:
$request->cookies->set('name', 'value');
echo \Cookie::get('name'); // output: 'value'

Hova is kellett ez nekem:
Egy middleware osztályba, ami a request handler lefutása előtt kellett, hogy beállítson néhány dolgot a cookie értékek közé.

Ui.: Valószínű, hogy van erre egy sokkal frappánsabb és egyszerűbb megoldás, de egy kevés google keresgélés után mindig a ::queue() metódusba futottam, de az éppen nem oldja meg a problémámat.

2018. augusztus 30., csütörtök

PHP_INI_SCAN_DIR környezeti változó a PHP-ban

Mire is jó ez a PHP_INI_SCAN_DIR?

Ha ez a környezeti változó be van állítva a PHP futási környezetében, akkor az ebben tárolt útvonalon megpróbál minden *.ini fájlt betölteni, amellyel felüldefiniálhatóak a korábban már beolvasott "php.ini" beállítások.

Bővebben angolul a PHP dokumentációjában: http://php.net/manual/en/configuration.file.php#configuration.file.scan

FONTOS:
Ne tévesszük össze a futtatható php parancssori kapcsolói közül a "-c <path>|<file>" lehetőséggel. Ez utóbbi arra való, hogy ha érvényes fájlra vagy könyvtárra mutat, akkor a php beállításokat tartalmazó fájlokat onnan próbálja beolvasni. Ha a -c kapcsoló nem mutat érvényes fájlra vagy könyvtárra, akkor a PHP az alapértelmezett helyek valamelyikén keres majd megfelelő ini állományt.

Ui.: A fenti változó nagyon hasznosnak fog bizonyulni a saját kis webszerveren további pofozgatásában, mert már meguntam, hogy az egyik PHP verzióban egy kicsit másképpen vannak beállítva olyan általános értékek, mint például a feltölthető maximális fájlméret (max_upload_filesize) vagy a POST kérések maximális mérete (post_max_size) és hasonlók.

2018. augusztus 24., péntek

FtpUse - Windows alatt FTP felcsatolása meghajtóként

Az FtpUse egy aprócska program, amit feltelepítve parancssorosan lehet meghajtó betűjelhez rendelni egy-egy ftp kapcsolatot.

Szerintem nagyon egyszerű a használata.

A programról bővebben a https://www.ferrobackup.com/map-ftp-as-disk.html URL címen.

Ui.: az első próbálkozások után nem igazán tudtam hordozható állapotra varázsolni, de ez valószínűleg a Dokan meghajtók miatt lehet.
A Dokan könyvtárról bővebben itt: https://dokan-dev.github.io/

2018. augusztus 14., kedd

Reguláris kifejezés: Az URL abszolút hivatkozás?

Reguláris kifejezéssel általában így szoktam tesztelni, hogy egy URL abszolút hivatkozás-e:
^((https?):)?//

A fenti kifejezés illeszkedik az alábbiakra:
http://www.example.com
https://www.example.com
//www.example.com
http://example.com
https://example.com
//example.com

De nem illeszkedik például az alábbiakra:
assets/page.js
/relative-from-document-root/assets/page.js


Magyarázat:
  • A szöveg elejére nézi az illeszkedést a "^" karakter
  • A szöveg elején 0 vagy egyszer szerepelhet a "https" karaktersorozat, amelyben az "s" karakter 0 vagy egyszer fordulható elő a "http" szöveg után
  • Az illeszkedő "http" vagy "https" szöveget követni kell egy ":" karakternek
  • Ha van illeszkedő "http:" vagy "https:" karaktersorozat a szöveg elején, akkor ezek 0 vagy egyszer fordulhatnak elő ("((https?):)?")
  • Ha volt protokoll illeszkedés, akkor utána legyen egy "//" karaktersorozat, vagy protokoll nélküli feltüntetés esetén a szöveg azonnal a "//" karaktersorozattal kezdődjön

www kiegészítés

Ha szükséges a "www." előtagra való illeszkedés is, akkor a fenti kifejezés kibővül az alábbira:
^((https?):)?//(www.)?


Használjátok egészséggel!

XSLT - minden másolása

Jó dolog ez az XSLT!

Például ha azt szeretnénk, hogy egy XML forrásból minden elem át legyen másolva a generált tartalomba, akkor a következő egyszerű és rekurzív XSLT sablont is lehet definiálnunk:

<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>

Röviden: 
A fenti szabály illeszkedik minden xml csomópontra és xml attributumra. Ha illeszkedik, akkor ugye 
lefut. 

Működése: az illeszkedő elemet (legyen az éppen aktuálisan egy csomópont, vagy attributum) másold át a cél dokumentumba. A másolás után alkalmazd újra az összes definiált XSLT sablon szabályt az aktuális csomóponton belül közvetlenül minden gyerek csomópontra, vagy attributumra.

Zseniális?

Update: egy angol nyelvű bővebb cikk a fentiekről: http://www.usingxml.com/Transforms/XslIdentity

2018. augusztus 12., vasárnap

PHP "arg_separator.output" beállítási lehetőség legyen inkább "&"

Előtörténet:

2018-at írunk, és van egy honlap, amely egy mondjuk valamennyire régebbecske tárhelyen foglal helyet. Kapott egy kis felújítást és a későbbiekben is fog kapni ezt-azt. Mondjuk, hogy ez egy olyan kis hobby oldalam. Elvállaltam.
A tárhely PHP 5.3.10. Nem mai darab, ezt azt hiszem ki lehet jelenteni.

Saját localhost-on szintén egy PHP 5.3.29 lett beállítva az oldal fejlesztéséhez. Lent minden tökéletes volt. Felmásoltam.

A probléma ott kezdődött, hogy egy oldalon volt egy átirányítás, ha nem voltak bizonyos url paraméterek a kérésben. A szükséges default url paraméterekkel készítettem egy url, és azt header() függvénnyel ki is lett küldve, ami lent tökéletesen működött.
A szükséges plusz paraméterek a http_build_query() függvénnyel lettek hozzáfűzve az új URL-hez, és egy Location: header kíséretében ki lett küldve az éterbe.

De az átirányított url-ben a "&" paraméterek helyett "&amp;" szerepelt. Ez így nem kóser, és elkezdtem kutatni.

A hibakeresés addig mindent rendben talált, hogy a http_build_query() függvény még helyes kimenetet ad.

Talán a header() függvényben van valami turpisság, illetve annak valamilyen beállítása?

...

Végül odáig fajult a dolog, hogy összehasonlítottam a lenti és a fenti phpinfo() kimenetét, azon belül a konfiuráiciós értékek listáját. Először tovább siklott a tekintetem, de utána ráakadtam a címben említett "arg_separator.output" beállítási lehetőségre, ami vajon mi volt az éles helyen? Hát persze, hogy "&amp;" és lent nálam pedig az "&" érték.

Mivel az éles tárhelyen nem férek hozzá a php.ini beállításokhoz közvetlenül, de szerencsére az ini_set() függvény nem lett letiltva, ezért ezen keresztül lett orvosolva a hiba.

Milyen kis apróság, és közel 1 órás nagyon izgalmas hibakereséssel gazdagította (?) az életemet.