Etikett Symfony

Uppdaterade nyheter

Hej kära vänner och läsare! Det är fredag och helg. Många av oss sätter oss i soffan ikväll med chips och tittar på något underhållande. De senaste veckorna har jag och min sambo kunnat njuta av den dumma (men roliga!) tv-serien Medical Police på Netflix. Tyvärr var det bara 10 avsnitt så nu får vi ägna oss åt något annat. Men eftersom det är fredag så skriver jag veckans sista inlägg så här på eftermiddagen. Den här veckan har fokus varit på programmering och att lägga till en blogg-funktion till Bokarenan. I onsdags skrev jag om hur Symfony och Doctrine har underlättat skapandet av en ny entitet (BlogPost) och uppdateringen av databasen. Denna gång ska jag skriva om den avslutande delen: hur allt ska kunna presenteras till användaren.

Det finns två komponenter att beakta när det kommer till hur nyheterna presenteras till användarna: template och Controller. Jag tänkte börja med Controller.

Min tanke är att den som är administratör ska kunna skriva ett blogginlägg (nyhet). Jag löste detta genom att använda AdminController och lägga till två funktioner till denna Controller som jag alltså har haft sedan tidigare. Den första funktionen innebär att administratören kan lägga till ett nytt inlägg. Något som jag vill lyfta fram som några detaljer är att administratören kan välja publiceringsdatum. Det innebär att inläggen inte dyker upp under Nyheter om inte publiceringsdatumet har nåtts. Den andra detaljen jag vill lyfta fram är ”slugs”. Det innebär att varje inlägg har en unik länk i stil med 2020-01-31-Det_här_är_en_nyhet. Detta kommer till användning när man vill titta på endast en nyhet, eller för sökmotorers möjligheter att länka direkt till ett inlägg.

Den andra Controllern som behövde uppdateras var NewsController. Denna kontroller har tilluppgift att presentera nyheter till användare. I kontrollern så skapade jag en ny funktion som hämtar de senaste inläggen och skickar dem till template-engine. Det tillkommer lite fler detaljer som innebär att jag kan paginera allt ihop och bara hämta de inlägg som ska vara på den aktuella sidan som användaren är på. Oftast är detta första sidan, men länkar till övriga sidor dyker upp längst ned.

Vad är då template-engine? Det är i princip en funktion som renderar html (eller php-filer med html-element) med dynamiskt innehåll. Som template-engine använder jag Twig. Från NewsController skickar jag de aktuella inläggen till Twig som löser det hela med att placera innehåll på rätt ställen och presenterar det med html-element och innehåll till användaren.

I NewsController så kan det se ut så här:

    /**
     * @Route("/{page}", name="nyheter_index", methods={"GET"})
     */
    public function index(BlogPostRepository $blogRepository, $page=1): Response
    {
        if (!\is_numeric($page)) {
            $page=1;
        }

        
        $count = $blogRepository->getCount();
        $limit = 1;
        $pages = $count/$limit;
        $pages = \ceil($pages);
        if ($page > $pages) {
            $page = $pages;
        }
        $posts = $blogRepository->getLatest($page, $limit);
        return $this->render("nyheter/index.html.twig", [
            "count" => $count,
            "page" => $page,
            "pages" => $pages,
            "posts" => $posts
        ]);
    }

Man kan se att det finns en template fil under filmappen nyheter som heter index.html.twig. Den filen ser ut så här:

{% extends "content.html.twig" %}
{% block navNyheter %}active{% endblock %}
{% block content %}
    {% for post in posts %}
        <article>
            <div class="article-header">
                <h1><a href="{{ path("nyheter_view", {'slug': post.slug}) }}">{{ post.title }}</a></h1>
            </div>
            <div class="article-content">
                <p class="time">{{ post.publish|date("d/m/Y") }} av {{ post.user }}</p>
                <p>{{ post.text|striptags('<em><strong><i><mark><cite><dfn>')|raw|nl2br }}</p>
                {% if app.user == post.user %}
                    <p><a href=""></a></p>
                {% endif %}
                
            </div>
        </article>
    {% endfor %}
    <div class="flex-center">
        {% for i in 1.. pages %}
            {% if i == page %}
                <a class="page-number active" href="/nyheter/{{i}}">{{ i }}</a>
            {% else %}
                <a class="page-number" href="/nyheter/{{i}}">{{ i }}</a>
            {% endif %}
        {% endfor %}
    </div>
{% endblock %}

Som man kan se i ovanstående kod så kan jag komma åt variabeln $posts genom instruktionen {{ post }}. Och när jag använder det på rätt sätt så kan jag få ett resultat som liknar det nedan.

Så det var allt för denna gången. Jag har skrivit ett blogginlägg om att göra en bloggfunktion för att göra blogginlägg. Blogg. Nu har jag skrivit det så mycket att jag börjar läsa det hela som grogg. Och apropå det: spel! Jag har visserligen en viss varm känsla för Secret of Monkey Island, men något som jag ser fram emot nu är att börja spela Skyrim igen. Senast jag gjorde det var 2013. Nu har jag den uppdaterade versionen på PS4. Jag får se om det fortfarande kan hålla mig underhållen. Men det är lugnt. Jag har några partyspel som nog funkar gott ändå!

Trevlig helg, allihopa!


Att göra en nyhetsblogg

Denna onsdag skriver jag om Bokarenan och möjligheten att bygga på den med fler funktioner. Det som står på dagordningen denna gång är en nyhetsblogg. För att få till detta så är det tre saker som ska komma på plats:

  • Ny entitet
  • Uppdaterad databas
  • Uppdaterad presentation/view

Det enklaste är att skapa en ny entitet med hjälp av MakerBundle i Symfony. Det innebär att jag via ett CLI (kommandotolken) kan instruera Symfony att göra den nya entiteten åt mig. På vägen får jag hjälp av Symfony för vad jag ska göra härnäst. För att starta det hela så utgår jag från projektets root-mapp och kör följande kommando:
php .\bin\console make:entity

Detta ger följande interaktion:

Jag döper min nya entitet till BlogPost, och ger den följande fält:
– title [string]
– text [text]
– entered [datetime]
– modified [datetime]
– publish [datetime]
– user [ManyToOne]

Exempel på hur fältet user i BlogPost relaterar till en annan entitet. User är det objekt som alltså refereras av BlogPost. ManyToOne innebär att respektive BlogPost bara kan referera till en användare, men en användare kan skapa flera BlogPost.

Genom hela denna process så har Symfony skapat och uppdaterat klassen BlogPost. Om vi tar en översikt över kursen så har den följande metoder och fält:

Då var steg ett avklarat. Nästa steg är att uppdatera databasen. Återigen är Symfony behjälplig… eller rättare sagt så är det faktiskt Doctrine. I CLI:t så kör jag följande kommandon:
php .\bin\console make:migration
Detta är en instruktion från Symfony till Doctrine hur databasen ska uppdateras efter att en till entitet har lagts till. Då återstår att låta Doctrine uppdatera databasen:
php .\bin\console doctrine:migrations:migrate
Databasen är nu uppdaterad och har rätta relationerna mellan BloggPost-tabellen och User-tabellen.

Då var två av tre steg avklarade. Det som återstår nu är att uppdatera presentationen för denna nya entitet. Detta steg består faktiskt av flera detaljer. Det innebär att en kontroller (eller två) ska uppdateras, en template för att skapa bloggposter ska skapas, en template för att presentera bloggposter ska uppdateras. Detta steg återkommer jag till på fredag. Vi ses då!


En tillbakablick på Bokarenan och Symfony

När jag började mitt projekt Bokarenan nu under hösten så hade jag ett mål att göra min kod mer hanterbar. Jag hade kommit till en punkt då jag inte kände att jag hade någon bra överblick över hur min kod hängde samman. Bokarenan var byggd med PHP men utan att använda någon objektorienterad struktur. Varje sida i applikationen var en egen PHP-fil. Min ”om” sida fanns till exempel på bokarenan.se/om.php, och min sökfunktion fanns på bokarenan.se/search.php. Eftersom man kunde logga in så behövde varje sida inkludera mina variables.php- och functions.php-filer. De användes för att dels hitta rätt filer (sökvägarna sparades i variables.php, bland annat sökvägen för functions.php) och dels för att kontrollera om användaren var inloggad och om användaren varit inaktiv för länge. Det var även viktigt att variables.php inkluderades före functions.php för att functions använde variabler i variables.php. Ju fler funktioner jag ville lägga till i applikationen, desto svårare blev det att överblicka att allting kom i rätt ordning. Jag kände att jag behövde göra min kod mer logiskt sammanhängande för att få rätsida på allt ihop. Svaret var Objekt Orienterad Programmering (OOP).

Jag är hyfsat bekant med OOP sedan allt jag gjort i Java har varit att jonglera objekt. Jag har till och med ägnat mig åt designprincipen MVC (Model – View – Controller) när jag prövat att göra en webapplikation i Spring Boot. Detta är ett framework för Java som jag gillade att använda. Men applikationen gjorde jag aldrig färdig för publicering. Kan hända att det projektet dyker upp här. Jag kallade den för Shipstorm och den var till för att dokumentera båtar som jag tyckte såg fina ut.

När jag nu ville göra en uppdaterad version av Bokarenan så ville jag göra OOP och gärna använda ett MVC-framework. Jag visste ju att det skulle hjälpa mig så mycket att få rätt struktur på allt ihop. Jag visste bara inte vart jag skulle hitta ett framework som skulle passa mig och som skulle kunna funka på mitt webbhotell.

Jag läste en del böcker om PHP samt en kurs på Packt Publishing som guidade mig helt okej igenom olika steg för att göra ett eget microframwork, för att sedan ta steget till att börja använda ett färdigt framework. Det blev Symfony till slut. Symfony upplevde jag som väldigt flexibelt. Det var enkelt att välja vilka komponenter av Symfony jag ville ha. Mina favoriter är Twig, Security och Doctrine. Twig gör det enkelt att generera sidor för dynamiskt innehåll. Security hjälper till med inloggning och autentisering. Och Doctrine gör det väldigt enkelt att hämta objekt från en databas.

Medan jag upplevt det som mycket enkelt att utveckla applikationen på min dator; den medföljande virtuella servern gjorde det hur smidigt som helst, så kvarstod utmaningen att få allt att funka på mitt webbhotell. Jag vill inte exponera känslig information så som databasuppgifter, men vill ju att besökare ska kunna nå min hemsida. Via mitt webbhotell så kan man logga in och använda deras filhanteringssystem. Nackdelen är att detta bara erbjuder den allmänna/publika sidan av hemsidan.

Lösningen hette FileZilla, ett FTP-program, som gjorde det möjligt för mig att även komma åt den yta som inte är publik. Jag kunde därmed dela upp min applikation i dess publika och dess privata delar. På den publika arean så la jag ingångsvägen för applikationen – index.php, samt CSS-, JS-, och bildfiler. På den privata arean la jag applikationslogiken; med Controllers, Entities och template-filer. Men bara detta gjorde inte applikationen redo för användning helt. Jag kunde nu ladda första sidan till Bokarenan på bokarenan.se. Men inga länkar funkade, även om applikationen nu kunde kommunicera med databasen. Det visade ju sig att bokarenan.se egentligen laddade bokarenan.se/index.php. Alla länkar gick genom denna fil. Så för att ladda till exempel bokarenan.se/om behövde jag ange bokarenan.se/index.php/om. Detta var inte riktigt snyggt i mitt tycke, så något behövde jag göra. Och in kommer RewriteEngine som jag kan komma åt med hjälp av .htaccess. Det är en fil jag lägger på min area och som instruerar servern till att översätta allting igenom index.php-filen utan att det ska synas i adressfältet.

Det har visserligen tagit mig hela hösten att uppdatera Bokarenan med Symfony och ersätta den spagettikod som utgjorde förra versionen, men jag har haft en rimlig anledning att dra ut på det: bebisar. Nu är allt uppe och funkar. Jag kan definitivt tänka mig att använda Symfony för fler projekt och det utgör en bra grund för att underhålla den nya versionen av Bokarenan.


Centrala gränsvärdessatsen

Äntligen en ny vecka! Jag har kommit ut på andra sidan av denna helg där jag mest suttit och slipat på Bokarenan. Jag kände att jag hade den tiden över efter att ha gjort en del plugg under den gångna veckan. Mycket tid har gått åt att slipa på CSS och skapa rimliga views (sidor) för de entiteter jag vill presentera. Jag har upplevt det som enklare att ha koll på vad jag skapar och var jag har det, nu när jag skapar mina views manuellt, istället för att låta Symfony skapa dem åt mig. De views som Symfony skapade var i klass med att sitta med phpMyAdmin, men mer begränsad. Så de färdigbakade sidorna motsvarade inte de syften jag hade med dem. Och jag börjar gilla vart åt det barkar.

För närvarande ser sidan ut som nedan. Jag har avvikit lite från hur jag hade skissat den i Adobe XD och gjort den generellt lite ljusare.

Men nog om programmeringen. Denna vecka fortsätter jag med statistik. Denna gång om centrala gränsvärdessatsen.

I förra veckan beskrev jag normalfördelning och hur bra det var att ha en sådan modell. Kortfattat så innebär det att man enkelt kan uppskatta fördelning av befolkning utifrån hur långt från medelvärdet man kollar baserat på standardavvikelse. Denna vecka säger jag att man kommer få en normalfördelning om man tar tillräckligt stora samplar (urval), i princip oavsett hur den verkliga populationen ser ut. Och det är inte helt negativt! Det man vill göra med att ta stora samplar är att komma så nära det verkliga medelvärdet som möjligt med så liten standardavvikelse som möjligt. Om man lyckas med det så har man fått ett urval som har ett medelvärde som ligger där det riktiga medelvärdet ligger, med liten felmarginal.

I sitt urval kommer man ha en standardavvikelse. Och om man vill säga något om 95% av populationen så fångas de inom 2 standardavvikelser från medelvärdet. Detta har jag skrivit om i förra veckan som 68-95-99.7% regeln. Så om standardavvikelsen är stor så kommer det intervall som beskriver 95% av populationen att vara stor. Förhållandet mellan populationens standardavvikelse och urvalets standardavvikelse är s = σ/(√n), där n är urvalsstorlek. Det innebär att man minskar standardavvikelsen med roten ur sampelstorlekens förändring. Vill man halvera standardavvikelsen så får man fyrdubbla urvalsstorleken.

Nästa gång ska jag skriva något om konfidensintervall. Jag misstänker att vi redan har börjat fila på det, men jag får återkomma när jag läst lite om saken.


Kommer i form

Sagan fortsätter. Senast hade jag problem med att uppdatera användares roller. Rollerna avgör vad användare kan göra på hemsidan. I huvudsak gäller det om en användare ska vara administratör eller inte. När jag skrev senaste inlägget så lyckades jag inte med att uppdatera användares roller och någonstans låg problemet i administratörskontrollen (controller) eller i formuläret. Jag visste inte exakt var problemet låg, men det gör jag nu: det var i båda!

Så jag är lite av en härmapa när jag håller på att lära mig nya saker, och när det kom till användare så härmade jag hur upplägget såg ut för att uppdatera till exempel författare. Och här är kruxet: upplägget för författare och användare är olika. För författare så färdiggenereras en formulärklass som man kan skicka till Twig, vilket är en så kallad template engine. Det innebär att utifrån givna parametrar så genererar Twig den html-kod som behövs för att visa givet innehåll. Och jag byggde upp en formulärklass för användare också, men använde inte Twig till att generera den. Istället så skräddarsydde jag html-koden för att motsvara exakt vad som behövs för att uppdatera användare med rätt roller. I ett parallellt spår med detta så byggde jag controller enligt samma sätt som controller för författare. Det innebar att controllern kontrollerade om det färdiggenererade formuläret innehöll någon ny data. Och eftersom jag inte använde mig av det färdiggenererade formuläret så innebar det att controllern inte fick någon uppdaterad data. Lösningen blev därför att uppdatera controllern med att kolla i svarsobjektet istället för i formulärobjektet. Och därmed hade jag på 5 minuter löst ett problem som hållit mig fast i ett par timmer.

Ytterligare utveckling denna veckan har varit att lägga till en aktiveringsprocess när man registrerar sig som ny användare. Det innebär att applikationen kommer kontrollera att den som registrerat sig också är användare av den epost som har angetts. På den fronten har det gått snabbt, men det beror också på att jag kunnat sitta i åtminstone 20 minuter åt gången för att programmera.

På återseende på fredag!


Snubblarmåndag

Så kör vi igång med en ny vecka. Målet för förra veckan var att få igång ett auktoriseringssystem, och till viss del har jag fått igång det. Målet denna vecka är att få klart det.

Varför blev det inte klart förra veckan? Jo, för att jag vet inte vilket problem jag har. Mer exakt vet jag inte varför min kontroll och formulär inte lyckas med att spara när jag ändrar typ av roll som en användare ska ha. Jag kommer återkomma i veckan med var problemet ligger någonstans och hur jag väljer att hantera det. Om jag inte lyckas med detta så återstår att jobba direkt mot databasen.

En annan anledning till att jag inte blivit klar är att jag för närvarande oftast får 10 minuter åt gången när jag kan sätta mig ned och programmera. Det är alldeles för korta stunder för att hinna sätta in mig i vad jag försöker göra. Vad jag istället hinner med är att teckna lite:

”Skakar hand.”

Alltså, det där är vad min hjärna kan göra när den har 10 minuter åt gången.

Nu hoppas jag att jag kan få mer gjort denna vecka, men det beror på sömn och bebisar. Annars blir det fler teckningar.


Auktorisering aktiverad

Så har det visat sig igen att det goda folket bakom Symfony kan göra bra guider. Jag har nu implementerat funktion för att avgränsa ytor av applikation till relevanta roller: så som att man måste vara inloggad som användare för att kunna göra inlägg, och som administratör så finns lite fler verktyg för att hantera entiteter. Det som återstår att göra är att skapa en super-administratör som kan lägga till roller för olika användare.

När jag är klar med super-administratören så finns det lite finlir jag behöver ta hand om. Till exempel behöver jag anpassa kontrollerna för entiteter så att de motsvarar hur jag vill använda dem. I och med dessa anpassningar behöver jag också justera form-klasser och presentationen av formulären.

Jag får se därefter hur mycket tid till övers jag kommer ha framöver. Snart kommer jag att börja en deltids kurs för dataanalys på grundläggande nivå. Det innebär att mina framtida uppdateringar från och med november kommer prata om statistik och att jag redan är inkörd på sömnbrist i och med bebisar.


Användarfunktioner

Vad är nyttan med användare om inte användare har funktioner till hands som inte besökare har? Det är ganska riskabelt att låta vem som helst få skapa innehåll på hemsidan, och därför vill jag avskärma applikationen till att bara kunna användas av folk som går med på lite grundläggande värderingar. Tanken är att Bokarenan är för vettiga och vänliga människor. Dessa medlemmar kan skapa recensioner till böcker som finns i databasen, och om den aktuella boken inte finns så ska de kunna skapa den. Men för att få detta att funka som jag vill så kan jag ta det som jag gjort hittills och avgränsa funktioner till att bara vara tillgängliga för de som loggat in. I tidigare version av Bokarenan så har jag skapat en SESSION COOKIE när användare loggar in, och därmed kan jag i headern för varje sida på applikationen kontrollera att det finns en användare närvarande. I och med att jag använder Symfony så finns det lite andra verktyg jag kan ta hjälp av. Och vilka verktyg är det då som finns? Jag vet inte! Inte än.

Denna vecka kommer jag därför att fokusera på Symfony:s verktyg för att hantera auktorisering. Min förhoppning är att jag kan skapa ett flexibelt system för att hantera olika roller och presentera relevant material och verktyg för användarna. Dessutom hoppas jag på att koden fortsättningsvis kan vara överblickbar och lätt att modifiera vid behov.


Fredagsform

Hur länge ska jag försöka få till formulär? Det kanske märks på mina blogginlägg att utvecklingen inte går snabbt, men det sker åtminstone små steg i rätt riktning varje dag. Det har att göra med konceptet att vara pappa till två bebisar. För när tid finns över till programmering så är frågan om jag är tillräckligt klar i huvudet för det. Men som sagt: det går framåt!

Jag har fått till ett formulär nu, som framgick av föregående inlägg. Den har rätt beståndsdelar, men är inte snygg och har ett blandat språk (på grund av anledningar). Detta kommer justeras längre fram. Det som är av största vikt när formuläret är på plats är att det nu ska finnas en kontroll som kan hantera formuläret och uppdatera databasen med formulär-datan.

Jag har använt mig av en hjälp-klass som jag kallar EntityBasket. Den har beskrivits i tidigare inlägg, men i stort så har den koll på hur formuläret är uppbyggt och kan hämta motsvarande entiteter ur den. Det innebär att en kontrollklass kan hämta en entitet och sedan skicka den till databasen. Det som min kontrollklass gör i detta sammanhang är att den hämtar samtliga relevanta entiteter från formuläret, skapar nödvändiga relationer dem emellan, och sedan sparar dem i databasen.

Det var inte lite förvånad jag blev när jag upptäckte att Doctrine (som är ramverket för databashantering i applikationen) var så lätt att använda som jag trodde. Doctrine gav mig entitets-id som jag kunde använda när jag ville skapa relationer mellan entiteterna, innan jag hade sparat dem mot databasen. Jag kunde ange för Doctrine vilka entiteter jag skulle spara och sedan sparades alla samtidigt när jag använda funktionen flush().

Så när jag nu fått till ”baksidan” av formuläret så tänkte jag härnäst få till att avgränsa funktioner att lägga till mot databasen. Endast användare som är inloggade ska få lägga till entiteter.

Och så går åter en vecka till helg. Den här veckan spelar jag annat än Elite Dangerous. Elite är ett spel som inte går att pausa när man spelar, vilket gör att man håller tummarna att inget händer när bebisar kallar. Och denna vecka har bebisar kallat mer än förr. Som omväxling till detta har jag tagit med dem till biblioteket och kollat på deras spelutbud. Jag fann spelet Control där. Spelet handlar om Jesse som lyckats hitta en hemlig myndighet; Federal Bureau of Control (FDC). Det som lett henne dit är försvinnandet av hennes bror. FDC var de som tog hennes bror ifrån henne när de var små, och hon har sökt efter dem sedan dess. Spelet är i tredje person, vilket utvecklarna Remedy är vana att göra spel i. Tidigare har de gjort Max Payne och Alan Wake, och denna gång är det som en mix av dem. Det är mycket action och mycket berättelse (särskilt i de rapporter som finns att hitta i spelvärlden). Jag gillar vad jag spelat hittills, och uppskattar att det går att pausa var som helst (även i filmsekvenser).

Glöm inte att det är kanelbullens dag denna fredag!

Trevlig helg, allihop!


Grunderna kommer på plats

Nu går det framåt, men är det snyggt? Med hjälp av Symfony så har jag fått formulär som är anpassade mot respektive entitet som jag sparar i min databas. Som jag tidigare beskrivit så vill jag att användaren ska kunna lägga till all relevant data när en recension ska läggas till. Det innebär att flera entiteter kan komma att läggas till när en recension läggs till, så som förläggare och författare. Min första lösning var att lägga till alla förproducerade formulär som Symfony skapat, men det innebar att ett antal fält skapades som förekom flertalet gånger (eftersom varje enskild entitet kunde relatera till en annan entitet). Jag tog därför och gjorde anpassade formulär för respektive entitet som avhandlade just den enskilda entiteten utan dess relation till andra entiteter. Resultatet blev något vackrare.

Så tillbaka till ett tidigare dilemma; hur ska jag implementera funktionaliteten att lägga till fält för fler författare? Ska jag använda min tidigare lösning som exkluderar JavaScript. Detta skulle innebära att sidan laddas snabbare och att jag undviker att använda sådant som jag inte är van vid. Nackdelen är att jag måste i så fall lägga till fält för upp till ett max antal författare för en bok, säg 10 stycken. Oavsett om användaren vill lägga till en författare eller 20. Det skulle kunna i udda fall kunna innebära att användare inte får lägga till så många författare som användaren vill.

Den andra lösningen, så som även beskrivs i en guide från Symfony, är att använda JavaScript och JQuery för att dynamiskt lägga till fler fält. Nackdelen är att källkoden inte blir vacker och sidan kan ta längre tid att ladda. Fördelen är att endast så pass många fält som användaren vill ha finns till hands. Det innebär också något tydligare indelning på ansvarsfördelning vad avser att JavaScript hanterar beteende och låter HTML stå för stommen.

Jag har valt den andra lösning, för att jag vill pröva något nytt och kanske lära mig något nytt. Resultatet nu är som sådant:

Det är tydligt att jag inte jobbat något med utseende, utan endast innehåll och funktion. När jag fått till de grundläggande funktionerna för applikationen så kommer jag ta hand om utseendet.