UBB Generator Part 3: De backend. OMDB Api en Slim Framework

Door Webgnome op maandag 28 december 2015 01:00 - Reageren is niet meer mogelijk
Categorie: UBB Generator, Views: 1.127

Zoals beschreven in de voorgaande delen maakt de UBB Generator gebruik van de OMDB api. Dit is een REST api waarmee het mogelijk is om van een groot aantal titels de detail informatie op te halen zonder gebruik te maken van de IMDB website of een scraper daarvoor.

Voor de API laag gebruik ik het Slim Framework

Slim framework

Met het slim framework is het mogelijk om vrij snel zonder poeha rest services op te zetten. Wat ik er zo sterk aan vind is dat het aardig OOP is opgezet en ook logisch in elkaar zit. Een voorbeeldje:


PHP:
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
$app = new \Slim\Slim($config);
$app->response->headers->set('Content-Type', 'application/json');

$app->get('/posters',function()use($app){
    
    $conn = new PDO("mysql:host=".$app->config('db.host').";dbname=".$app->config('db.name').";charset=utf8", $app->config('db.username'), $app->config('db.password'));
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    $stmt = $conn->prepare('SELECT * from posters order by rand()');
    $posters = array();
    if($stmt->execute()){
        while($row = $stmt->fetch(PDO::FETCH_BOTH)){
            $poster = array();
            $poster['imdbid'] = $row['imbdid'];
            $poster['image'] = $row['fileid'].".jpg";
            $poster['imdburl'] = "http://www.imdb.com/title/".$row['imbdid'];
            $posters[] = $poster;
        }
    }
    $app->response->setBody(json_encode($posters));
    $app->response->setStatus(200);
    
});

$app->run();



Wat zien we hier. Als eerste geven we aan dat we een nieuwe Slim willen opzetten met een predefinied $config. Dit is een array die ik in een aparte PHP file heb staan en waar de configuratie in zit zoals de DB username en passwords etc. Er zijn een aantal standaard configuraties mogelijk (zie de slim site) maar je kunt er ook je eigen indexes aan toevoegen en dat maakt het mogelijk om db settings te gebruiken of andere non slimframework settings.

Vervolgens setten we een standaard header omdat we weten dat de content altijd JSON zou moeten zijn. Vanaf regel 4 begint het echte werk. We definieren daar een GET actie. De parameters zijn als volgt. Het endpoint ( op welke url moet deze getter reageren) en de functie die aangeroepen wordt op het moment van aanroep op het endpoint url.

In deze functie kunnen we vervolgens onze dingen doen in setten we de body en de status. Simple as that.

Een ander voorbeeld is deze:


PHP:
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
$app->get('/movie/:imdb',function($q)use($app){
    $q = htmlentities($q);
    $response = file_get_contents("http://www.omdbapi.com/?i=".$q);
    $omdbResponse= json_decode($response);
    
    $conn = new PDO("mysql:host=".$app->config('db.host').";dbname=".$app->config('db.name').";charset=utf8", $app->config('db.username'), $app->config('db.password'));
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    $stmt = $conn->prepare('SELECT count(*) as c from posters where imbdid = :id');
    $stmt->bindParam(':id',$q);
    $filename = null;
    
    if($stmt->execute()){
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        $count = $row['c'];
        
        if($count == 1){
            $stmt = $conn->prepare('SELECT `fileid` from posters where imbdid = :id');
            $stmt->bindParam(":id",$q);
            if($stmt->execute()){
                $row = $stmt->fetch(PDO::FETCH_ASSOC);
                $filename = $row['fileid'];
            }
        }
    }
    
    if($filename == null){
        
        $filename = "noimage";
        if(property_exists($omdbResponse,'Poster')){
            $posterUrl = $omdbResponse->Poster;
            if($posterUrl != "N/A"){
                $smallPosterUrl = str_replace("SX300.jpg","SX173.jpg",$posterUrl);
                $imageData = file_get_contents($smallPosterUrl);
            
                $fileName = bin2hex(openssl_random_pseudo_bytes(16));
                file_put_contents($app->config('imagepath').$fileName.".jpg", $imageData);
                $stmt = $conn->prepare('INSERT INTO posters(`imbdid`,`fileid`) values(:imdb,:file)');
                $stmt->bindParam(":imdb",$q);
                $stmt->bindParam(":file",$fileName);
                if($stmt->execute()){
                    $filename = $fileName;
                }
            }
        }
        
    }   
    

    $omdbResponse->Poster = "images/".$filename.".jpg";
    
    $app->response->setBody(json_encode($omdbResponse));
    
});



We zien hier een GET methode met een parameter :imdb. Dit parameter wordt gemapped naar variabel $q. Voor de rest is er niets spannends aan deze methode en doen we eigenlijk hetzelfde als altijd. Aan het eind van alle endpoint definities dien je nog wel de $app->run() niet te vergeten anders doet je API natuurlijk als nog niets.

Tot zover

UBB Generator Part 2: De UI, Angular

Door Webgnome op zondag 27 december 2015 01:00 - Reacties (5)
Categorie: UBB Generator, Views: 1.418

Deel tweein de serie over de totstandkoming van de UBB generator focus ik mij vooral op de UI kant van het verhaal en dan met name Angular.

Ik heb gekozen om gebruik te maken van Angular Material omdat ik zelf geen graficus ben (of UX designer zoals dat tegenwoordig mooi heet) en toch iets representatiefs wilde neerzetten. Zelf ben ik wel fan van het Material design principe dus vandaar deze keuze. Angular Material komt met en groot aantal standaard directives die het mogelijk maken om snel een UI in elkaar te zetten.

Zoals aangegeven is de AppCtrl controller de controller die de zoekbalk en het tonen van de juiste 'card' aanstuurt. De manier waarop dat dit gebeurt is door gebruik te maken van zgn. $watch functies. Een $watch functie kun je op de $scope toevoegen om te zorgen dat bepaalde variabelen in de gaten worden gehouden. Je dient een functie te schrijven die twee paremeters bevat. Ik noem ze eigenlijk altijd n en o (new/old). Omdat deze $watch altijd minimaal één keer wordt afgetrapt dien je te controleren of dat de variabelen hetzelfde zijn (bij de eerste keer zijn de variabelen namelijk altijd beide 'null' ) en als dit niet het geval is dan kun je dingen gaan doen.

Voorbeeld van een dergelijke $watch is in het in de gaten houden welke service er gebruikt moet worden door de zoekbalk:


JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$scope.$watch('searchType',function(n,o){
        switch(n){
        case 'series':
            $scope.service= seriesService;
            $scope.searchPlaceholder = "Search a serie by title or IMDB id (tt..)";
            break;
        default:
            $scope.service = movieService;
            $scope.searchPlaceholder = "Search a movie by title or IMDB id (tt..)";
        }
        
        $scope.searchText = '';
        
    });



In dit voorbeeld controleer ik of de nieuwe waarde series is en zo niet dan wordt er een andere service op de scope gezet en de placeholder tekst voor de zoekbalk aangepast. Ik ben zelf wel gecharmeerd van deze manier van aanpak. Zo kun je in de controllers met een paar regels code functionaliteit schrijven die veranderd als je scope variabelen veranderen.

Directives

Op een bepaald moeten in de ontwikkeling ben ik begonnen met het gebruik van Directives. Directives zijn kleine stukken code / HTML die een bepaald onderdeel van je applicatie bevatten. De manier waarop ik de directives gebruik is door simpelweg zgn. partials in te laden op het moment dat ik het nodig heb.

Zo zijn er naast de material directives een ubbcard directive en een moviecard directive. Er zit hier niet heel veel spannends in behalve dan dat de controller in een andere angular module staat dan de directive. Dit wil ik in de toekomst nog aanpassen.

Services

Zoals gezegd maak ik gebruik van een aantal factories. De reden waarom ik een factory en geen service gebruik is omdat ik gelezen heb in de documentatie dat wanneer je een service maakt er iedere keer een nieuwe instantie gemaakt wordt je van je object en je dan ook in de knoop kan komen met caching. Dit heb ik zelf niet getest dat staat nog op het todo lijstje.

De manier waarop ik mijn factories maak is als volgt:


JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
services.factory('posterService',['$http','$q',function($http,$q){
    var s = {};
    s.cache = null;
    s.baseUrl = "api.php/posters";
    
    s.getPosters = function(){
        var def  = $q.defer();
        if(this.cache == null){
            $http.get(this.baseUrl).then(function(data){
                this.cache = data.data;
                def.resolve(this.cache);
            },function(data){
                def.reject(data);
            });
            return def.promise;
        }else{
            def.resolve(this.cache);
        }
        
    }
    return s;
}
]);



Om te beginnen definiëren we natuurlijk de factory door het een naampje te geven en de nodige dependencies. Het eerst volgende dat in de functie gebeurt is een leeg 'object' aan te maken waar ik vervolgens alle properties aan toevoeg. Dit vond ik zelf een leesbare manier van objecten opzetten in javascript. Als laatste stap return ik deze s.

Zo kan ik in mijn controllers dus simpelweg service.getPosters() afroepen en de nodige dingen doen. Zoals je ziet maak ik ook gebruik van $q. $q kan gebruikt worden om de promise die $http genereert door te gooien en zodoende in de call van $http nog wat dingen te doen voordat je de promise door geeft. Het voordeel hiervan is is dat je in de service laag de return waarde van $http kan manipuleren voordat je deze de controller in schiet. In de controller kun je dan nog steeds met service.doStuff().then(function(){},function(){}); blijven werken.

Een voorbeeld hiervan zou kunnen zijn dat je de data die je binnen krijgt wilt verrijken met data uit een andere call. Vervolgens als die laatste call goed gaat geef je de $q.defer() terug.

UBB Generator Part 1: De Architectuur

Door Webgnome op vrijdag 25 december 2015 01:00 - Reageren is niet meer mogelijk
Categorie: UBB Generator, Views: 1.810

Dit is het eerste deel in mijn reeks om uitleg te geven over hoe de UBB Generator is ontstaan en hoe deze in elkaar zit. Het is vooral voor een showcase om te laten zien wat mogelijk is.

De applicatie bestaat functioneel gezien uit aantal onderdelen namelijk:
  • Een zoekbalk waarmee naar films / series gezocht kan worden op titel of IMDB ID
  • Een lijst van gemaakte reviews
  • Een scherm waarmee het mogelijk is een review te schrijven met support voor veelgebruikte UBB Codes
  • Een scherm waar de gegenereerde UBB Code vanaf kan worden gekopieerd met één druk op de knop
Technisch gezien is het een stukje ingewikkelder:

Het is een zogenaamde one-page AngularJS app. Dat wil zeggen dat door middel van dom manipulatie een applicatie ontwikkeld is die, vanuit een browser standpunt, vanaf 1 enkele pagina draait. Heel veel zaken gebeuren client side .Dit heeft als belangrijke voordelen dat er minder requests naar de server moeten. De requests die gedaan worden zijn alleen om data op te halen of om data op te slaan.

de AngularApp bestaat uit de volgende onderdelen

Directives

Toolbar
UbbCard
MovieCard
ReviewList

Deze losse directives hebben allemaal hun eigen controller. De uitzondering op deze regel is de toolbar. Deze wordt aangestuurd door de AppCtrl controller. De AppCtrl controller is verantwoordelijk voor de overkoepelende acties. Zoals het switchen van movie naar ubbcard en terug. Of het in gang zetten van de search en het tonen van de juiste directive.


Services

De applicatie kent een aantal services die allemaal gebruikt worden voor het aanroepen / gebruiken van de API laag.

ReviewService

Dit is de belangrijkste service. Deze bevat namelijk de functionaliteit om van de ingetypte review ubbcode te kunnen maken. Maar wordt ook gebruikt door de ReviewList directive om bij te houden welke reviews er geschreven zijn in de huidige sessie en het manipuleren van deze lijst.

MovieService
De movieservice , zoals de naam al zegt, wordt gebruikt daadwerkelijk film informatie op te halen vanuit de api. De enige twee calls die er nu beschikbaar zijn , zijn het zoeken naar en het kunnen ophalen van detail informatie.

SeriesService
Een kopie van de MovieService waarbij de endpoints anders zijn. De reden om deze los te trekken is omdat ik verwacht in de toekomst wat serie specifieke zaken toe te voegen.

PosterService
Om de Gallery pagina te kunnen tonen heb ik een aparte service in het leven geroepen die van alle opgeslagen filmposters een mooie JSON terug geeft met het imdbID en de filename. Hierdoor kan ik een mooie grid tonen van alle posters die ooit zijn opgevraagd.

StylingService
De naam is een beetje verwarrend maar deze service wordt gebruikt om de UBB tags toe te voegen aan de review.

Api

De movieservice, seriesservice en posterservice maken allemaal gebruik van de zelf ontwikkelde api. Deze is geschreven in PHP en maakt gebruik van het slim framework. Dit framework geeft je de mogelijkheid om snel een simpele rest api op te zetten. De API die opgezet is is eigenlijk een wrapper rondom de OMDB Api. De reden dat ik de OMDB API gebruik is vrij simpel. Het scrappen van de IMDB site is zo foutgevoellig dat ik het liever overlaat aan een onofficieuze JSON api die wel altijd werkt.

De Api zorgt er tevens voor dat posters niet elke keer opnieuw worden opgehaald uit OMDB maar dat ze gecached worden en getoond vanuit cache als dat mogelijk is.

Tot zover de uitleg over de structuur. In een volgende aflevering zal ik wat meer ingaan op de code van de angular kant van het verhaal.

UBB Generator

Door Webgnome op woensdag 23 december 2015 19:48 - Reacties (4)
Categorie: UBB Generator, Views: 2.850

Zoals menigeen van jullie weet wordt er sinds kort in het Welke film(s) heb je laatst gezien en wat vond je? topic gebruik gemaakt van de door mij gemaakte UBB Generator. In de komende posts wil ik hier verder op in gaan. Vooral de manier waarop deze tot stand is gekomen. Maar ook de punten waar ik tegenaan gelopen ben tijdens de ontwikkeling.

Voor0dat ik deze serie ga starten even een kleine update. Er is nu ook een Gallery pagina waar je de filmposters kunt zien van alle films waarvan informatie is opgevraagd via de Generator.

Wat doet de UBB Generator?
Zoals de frequente GoT bezoeker wel weet kun je gebruik maken van tags om je tekst te voorzien van opmaak. Deze tags worden ook wel UBB tags genoemd. De UBB Generator is ooit ontwikkeld door Twylight and Wiethoofd om voor het benoemde topic een mooie opmaak te laten genereren voor de gebruiker.

De gebruiker schreef zijn review , voegde daar zelf wat opmaak tags aan toe en vervolgens werdt er een ubb tag opmaak gegenereerd zodat wanneer je deze plakt in het topic je een eenduidige opmaak krijgt van reviews.

Inhoud van de UBB Generator series: