Ik werk momenteel aan een redelijk groot PHP project gebouwd met Symfony 3, Doctrine en Twig en ik heb bij redelijk wat zaken het gevoel dat ik er een spaghettiboel van aan het maken ben door gebrek aan inzicht. Eigenlijk is dit een architecturele vraag die in het algemeen binnen MVC/DDD van toepassing is en niet zozeer Symfony/Doctrine op zich. Maar goed, hier is mijn probleemstelling:
Het belangrijkste en meest centrale deel van de applicatie is een "zoekmachine" die kan zoeken binnen een database van verzamelkaarten. Het gaat hier niet zozeer om tekst, maar om eigenschappen van de te zoeken kaart(en) in kwestie zoals "kaart types", "kaart effecten", "kaart aanvallen",... Vergelijk het met bijvoorbeeld coolblue waar ja een ganse lijst filters met checkboxes hebt. In totaal zijn er zo een 30 verschillende soorten filters, waarvan de meeste eigenlijk gewoon een array van IDs zijn.
Als entity voor kaarten heb ik een "Card" klasse, die gelinkt is met een "CardRepository" waarmee de kaartdata geraadpleegd kan worden, in mijn geval een MySQL database. Omdat de standaard repository methodes (find, findAll, findBy,...) niet volstaan voor een complexe zoekopdracht, heb ik een custom methode "search" in de repository aangemaakt. Deze methode heeft in totaal 5 parameters, zijnde:
Zoekcriteria
Voor de zoekcriteria heb ik een CardSearchCriteria klasse gemaakt met alle mogelijke 30 filtereigenschappen, waar getters en setters voor voorzien zijn.
Sorteeroptie
De sorteeroptie is is een string uit een lijst van 5 mogelijkheden, die als publieke constante (array) binnen de CardRepository gedefinieerd is.
Array van Card velden die prefetched moeten worden
Omdat het resultaat van Cards binnen een overzichtstabel getoond moet worden, moeten sommige velden van Card "eagerly" geprefetched worden samen omdat Doctrine anders voor elke kaart in de tabel hier een nieuwe query voor uitvoert (standaard lazy loading).
Limit en offset
Deze worden gebruikt om het resultaat te pagineren.
Wat de search methode zelf betreft, is deze echt gigantisch. Alle 30 filters worden afgelopen en conditioneel aan een queryBuilder toegevoegd. Bijvoorbeeld met dit stuk code pas ik de gegeven sorteeropties toe, wat eigenlijk maar een klein deel is:
Het resultaat van de search methode wordt teruggegeven als een "CardSearchResult" object dat oa een array van Cards bevat en het totaal aantal gevonden kaarten (getal voor paginatie).
Tot slot komt uiteindelijk de oorzaak waarom ik dit topic wou plaatsen. Ik zou namelijk uit de zoekmachine een lijst van actieve filters willen terugkrijgen, maar heb hierbij het gevoel dat het extreem complex en omslachtig begint te worden.
Ik wil dus aan de eindgebruiker een lijst van actieve filters tonen, en hiervoor zou ik binnen de search methode oa de entities moeten fetchen die overeenkomen met de IDs uit de criteria. Bij het genereren van de HTML kan ik dan de nodige getters van de entities aanspreken om de informatie te tonen (bv card type: ultra rare).
Mijn oorspronkelijke idee was om hier een "CardSearchActiveFilters" klasse te voorzien met alle nodige getters/setters, maar mijn frustratie met deze aanpak is dat ik binnen mijn view/html elke getter manueel moet checken. Het zou veel eenvoudiger zijn binnen de search methode een simpele array te bouwen die alle informatie heeft zodat ik deze eenvoudig kan oplijsten binnen een loop in de view. Om elk item binnen de array consistent te houden, ben ik praktisch verplicht logica die in de view zou moeten in mijn databaselaag onder te brengen (wat ik dus niet wil), dus dit lijkt ook niet echt een optie.
Ik vind het geheel gewoonweg enorm omslachtig/vuil aanvoelen, alleen al omdat bij elke kleine verandering aan de filters ik op 10 plaatsen aanpassingen moet maken (het parsen van de querystring naar het criteria object, het implementeren van de querybuilder zelf, het samenstellen van de actieve filters, het tonen binnen de view,...)
Ik werk op mezelf en mijn frustratie is dat ik geen flauw idee heb of ik fouten maak, of mijn manier van werken verbeterd kan worden. Mijn excuses voor deze lange post, maar ik zou graag wat feedback hebben van mensen met meer kennis over mijn manier van werken, want ik heb momenteel het gevoel dat ik er een grote spaghettiboel van aan het maken ben.
Het belangrijkste en meest centrale deel van de applicatie is een "zoekmachine" die kan zoeken binnen een database van verzamelkaarten. Het gaat hier niet zozeer om tekst, maar om eigenschappen van de te zoeken kaart(en) in kwestie zoals "kaart types", "kaart effecten", "kaart aanvallen",... Vergelijk het met bijvoorbeeld coolblue waar ja een ganse lijst filters met checkboxes hebt. In totaal zijn er zo een 30 verschillende soorten filters, waarvan de meeste eigenlijk gewoon een array van IDs zijn.
Als entity voor kaarten heb ik een "Card" klasse, die gelinkt is met een "CardRepository" waarmee de kaartdata geraadpleegd kan worden, in mijn geval een MySQL database. Omdat de standaard repository methodes (find, findAll, findBy,...) niet volstaan voor een complexe zoekopdracht, heb ik een custom methode "search" in de repository aangemaakt. Deze methode heeft in totaal 5 parameters, zijnde:
Zoekcriteria
Voor de zoekcriteria heb ik een CardSearchCriteria klasse gemaakt met alle mogelijke 30 filtereigenschappen, waar getters en setters voor voorzien zijn.
Sorteeroptie
De sorteeroptie is is een string uit een lijst van 5 mogelijkheden, die als publieke constante (array) binnen de CardRepository gedefinieerd is.
Array van Card velden die prefetched moeten worden
Omdat het resultaat van Cards binnen een overzichtstabel getoond moet worden, moeten sommige velden van Card "eagerly" geprefetched worden samen omdat Doctrine anders voor elke kaart in de tabel hier een nieuwe query voor uitvoert (standaard lazy loading).
Limit en offset
Deze worden gebruikt om het resultaat te pagineren.
Wat de search methode zelf betreft, is deze echt gigantisch. Alle 30 filters worden afgelopen en conditioneel aan een queryBuilder toegevoegd. Bijvoorbeeld met dit stuk code pas ik de gegeven sorteeropties toe, wat eigenlijk maar een klein deel is:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| switch ($sortBy) { case 'expansionsDesc': $queryBuilder ->addOrderBy('expansion.releasePeriodBegin', 'DESC') ->addOrderBy('expansion.id', 'DESC') // Order by ID in case multiple rows have same release date. ->addOrderBy('card.numberOrder', 'ASC'); break; case 'expansionsAsc': $queryBuilder ->addOrderBy('expansion.releasePeriodBegin', 'ASC') ->addOrderBy('expansion.id', 'ASC') // Order by ID in case multiple rows have same release date. ->addOrderBy('card.numberOrder', 'ASC'); break; case 'cardNameAsc': $queryBuilder->addOrderBy('COLLATE(card.name, UTF8_GENERAL_CI)', 'ASC'); break; case 'cardNameDesc': $queryBuilder->addOrderBy('COLLATE(card.name, UTF8_GENERAL_CI)', 'DESC'); break; default: throw new Exception(sprintf('Invalid sort by option "%s".', $sortBy)); } |
Het resultaat van de search methode wordt teruggegeven als een "CardSearchResult" object dat oa een array van Cards bevat en het totaal aantal gevonden kaarten (getal voor paginatie).
Tot slot komt uiteindelijk de oorzaak waarom ik dit topic wou plaatsen. Ik zou namelijk uit de zoekmachine een lijst van actieve filters willen terugkrijgen, maar heb hierbij het gevoel dat het extreem complex en omslachtig begint te worden.
Ik wil dus aan de eindgebruiker een lijst van actieve filters tonen, en hiervoor zou ik binnen de search methode oa de entities moeten fetchen die overeenkomen met de IDs uit de criteria. Bij het genereren van de HTML kan ik dan de nodige getters van de entities aanspreken om de informatie te tonen (bv card type: ultra rare).
Mijn oorspronkelijke idee was om hier een "CardSearchActiveFilters" klasse te voorzien met alle nodige getters/setters, maar mijn frustratie met deze aanpak is dat ik binnen mijn view/html elke getter manueel moet checken. Het zou veel eenvoudiger zijn binnen de search methode een simpele array te bouwen die alle informatie heeft zodat ik deze eenvoudig kan oplijsten binnen een loop in de view. Om elk item binnen de array consistent te houden, ben ik praktisch verplicht logica die in de view zou moeten in mijn databaselaag onder te brengen (wat ik dus niet wil), dus dit lijkt ook niet echt een optie.
Ik vind het geheel gewoonweg enorm omslachtig/vuil aanvoelen, alleen al omdat bij elke kleine verandering aan de filters ik op 10 plaatsen aanpassingen moet maken (het parsen van de querystring naar het criteria object, het implementeren van de querybuilder zelf, het samenstellen van de actieve filters, het tonen binnen de view,...)
Ik werk op mezelf en mijn frustratie is dat ik geen flauw idee heb of ik fouten maak, of mijn manier van werken verbeterd kan worden. Mijn excuses voor deze lange post, maar ik zou graag wat feedback hebben van mensen met meer kennis over mijn manier van werken, want ik heb momenteel het gevoel dat ik er een grote spaghettiboel van aan het maken ben.