Webbutvecklingsdagbok - efter studierna

Permalänk

Webbutvecklingsdagbok - efter studierna

Tjo igen! Efter avslutade studier i min tidigare Webbutvecklingsdagbok samt en recension av distansutbildningen så öppnar jag nu denna Webbutvecklingsdagbok vilket även innebär ett "uppgraderat" användarnamn😎! Jag är numera WebbkodsFrilansaren eller WKF. Med detta sagt så kommer jag fortfarande att lära mig nya saker och ting.

(Ett mycket stort tack till SweClockers för den mycket snabba ändringen efter min förfrågan - faktiskt redan idag på bara någon timme eller två!🤯 Förhoppningsvis är mitt "uppgraderade" användarnamn endast en främmande nyckel och inte något hårdkodat i någon databastabells nycklar som skulle orsaka plötsliga framtida SQL-fel!😁)

Den kommer att täcka följande löpande:
1. Jobb & uppdrag - om mina jobb- & uppdragssök och eventuella pågående jobb och/eller uppdrag.

2. Hobbyprojekt - om mina kodprojekt utanför arbetsplatser och/eller uppdrag.

3. Vidareutbildning - om mina vidareutbildningar på egen hand och/eller via diverse utbildningsinstitutioner.

I och med denna tråd så kommer jag slippa skapa nya trådar om vad jag pysslar med och/eller framtida utbildningar jag går. Allt kan nu samlas i ett och samma "trådramverk(?)"😂.

Jobb & Uppdrag
Jag spekulerade i min tidigare studietråd om att ett bra arbete med en nöjd exjobbgivare skulle eventuellt leda till en framtida uppdragsgivare. Detta har nu inträffat. För någon dag sedan skrev min före detta exjobbgivare - hädanefter hänvisad som uppdragsgivare - på en offert för ett arbete motsvarande 8 arbetsveckor.

Nu på fredag ska vi diskutera och skriva på ett ytterligare avtal som omfattar mer eller mindre exakt vad dessa 8 arbetsveckor ska utgöra i rena webbsidor och all funktionalitet på dessa utifrån diverse underlag som är sekretessbelagt vilket jag därför inte kan gå in på specifikt.

Vad jag kan säga är att det lutar mot att använda PHP, MySQL/MariaDB igen och möjligen en hel del JavaScript för att uppfylla vad som är tänkt att åstadkomma med detta allra första uppdrag från min allra första uppdragsgivare direkt efter studierna! En utmaning är att jag måste förhålla mig inom webbservermiljön som är Loopia då ingen VPS går att/fås använda i dagsläget.

Således har jag funderat på i vilken utsträckning ReactJS skulle kunna användas för att underlätta användningen av de interaktiva bitarna jag måste utveckla eller om klassisk hederlig $_POST kan fixa biffen igen? Det sistnämnda kan innebära en irritation om det blir många POST-anrop och att webbsidan laddas om istället för att enbart innehåll byts ut likt ett SPA. Värt att diskutera med uppdragsgivaren om.

Hobbyprojekt
Under examensarbetet för ett par månader sedan så upptäckte jag att jag upprepade mig en hel del för att ta fram en konsekvent säker webbplats åt min före detta exjobbgivare. Detta ledde mig till att vilja ta fram ett funktionsbaserat PHP-ramverk bara för att det är/vore kul.

Det funktionsbaserade PHP-ramverket (vilket alltså använder sig av nästan bara funktioner istället för klasser där det går, t.ex. SQL-databasanslutningen är ett objekt) kallar jag för Router, Data, Page eller "RDP" för jag tänker mig att det är sekvensen för alla HTTP(S)-anrop: Du anger en URI, eventuella data hämtas, en eventuell webbsida visas.

Flera kontroller kan göras under val av URI (t.ex. behörigheter, sessioner, existerar URI:n ens, eventuella felsidor för vissa URI:s men inte andra, och så vidare), under val av data (behörigheter, sessioner, eventuella felsidor när data saknas eller dölja det pga. säkerhetsskäl, och så vidare), under val av webbsida (behörigheter, sessioner, eventuella felsidor när data saknas eller dölja det pga. säkerhetsskäl eller är den slutgiltiga responsen kanske bara en fil, JSON eller någon annan form av payload?

Jag har fått till dynamiska routes: ['GET/test/{id}' => ['text/html', 'all']] där {id} då kan vara vad som helst och denna sparas även som en parameter om den vill användas vid steget av datainhämtning. Nu filar jag på att få till sessionen och behörigheten vilket är under andra arrayelementet 'all'.

När det står 'all' så innebär det i princip att ingen behörighet och/eller session behövs för att komma åt just denna route. Jag har redan databastabeller och ett par funktioner som kan göra kontroller som att inte bara kontrollera inloggningssessioner utan också så att den som gör förfrågan anropar med en av flera giltiga IP-adresser eller så ignoreras IP-adresskontroll för just den användaren.

Huruvida jag försöker mig på ett simpelt ORM återstå att se. Jag har en saftig funktion som kan ta emot vilken slags SQL Query (CRUD eller om du bara vill göra något så simpelt som att kontrollera SQL-användarens behörighet, alltså inte SELECT, DELETE, INSERT eller UPDATE), sedan parametrar som binds (statement med binded params används) och sedan genomförs anropet och du kan ange hur du vill ha tillbaka dina data om det är SELECT eller om du vill ha tillbaka senast insatt radidentifieringsnummer om det är INSERT.

Det sista steget för varje anrop som är att välja rätt webbsida eller payload att svara med kommer att få en enkel Template Engine med inspiration från Laravel. Jag har hittat en video som visar hur man kan gå tillväga med detta via preg_replace() och så klart kommer jag INTE att använda mig av eval()😱.

En intressant sak i det sista steget är just det här med caching. Beroende på undersida och hur ofta data kanske uppdateras så kan det ju vara önskvärt att ha olika cachingtider för olika slags webbsidor? "Mallmotorn" gör ju sitt anspråk på processorkraft vid varje HTTP(S)-anrop så webbsidor som inte har så mycket sådan syntax i sig är ju inte värda att köra om hela tiden om det istället kan finnas statiska filer för just vissa sådana slags webbsidor.

Om någon har några tips & tricks kring hela hobbyprojektet att utveckla ett cirka 99 % funktionsbaserat PHP-ramverk, så är det bara att svara här vid lust och tillfälle!

Vidareutbildning
Jag håller på att läsa en amerikansk bok om Datornätverk där jag nu läst färdigt kapitlet om Applikationslagret och snart även Transportlagret. Boken går från mjukvarurelaterade lager såsom Applikationslagret och sedan ned till hårdvarurelaterade lager såsom Fysiska lagret.

Detta gör jag för att jag känner att jag är som en elektriker som kan dra kablar i hus men inte har en aning om hur elen kommer från elkraftverken och in i de olika hemmen och hur elen kanske tar olika vägar, störs på vägen till husen, eller andra bra saker att känna till om dess färd till slutdestinationerna.

I mitt fall handlar det om hur nätverkspaket skickas från exempelvis en server och sedan hamnar hos min personliga dator i form av någon payload som ska tolkas (eng. parsing) som exempelvis en HTML-fil i en webbläsare som använder sig av HTTPS-protokollet inom Applikationslagret.

En intressant sak jag läste är att per standard så ska routrar inte bry sig om paketens innehåll utan bara skicka vidare dem utifrån destinationsadress och port. Då tänkte jag, "Men det finns väl inget som säger att vissa routrar skulle kunna 'paketsniffa'?" Och detta faktum besvarar väl kanske varför vissa bolag inte tillåts få utgöra en del av Sveriges kommande nationella nätverk? 🤔

Just nu i kapitlet om Transportlagret läser jag om TCP-protokollet och hur det faktiskt ser ut på paketnivå med alla dess huvuden, och all data som skickas i varje paket. Detta kommer underlätta mig att senare börja sätta mig in - på nybörjarnivå - i Wireshark för att lära mig vad jag kan (och inte kan) göra med webbplatser för att säkra upp dem mot diverse IT-relaterade hot.

På återseende!

Mvh,
WFL (f.d. WKL).

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Grattis till första uppdraget! Får man fråga vart du satte dig prismässigt för 8 veckor / 320h arbete?

Permalänk
Medlem

Coolt hobbyprojekt och kul att du vill lära dig Wireshark. Men hos klienter bör du hålla dig till etablerade ramverk (react, angular etc) så får du mycket av säkerheten gratis. Klienten får också lättare att underhålla applikationen i framtiden.

O andra sidan, lyckas du låsa in dem till ditt egna ramverk så har du säkrat upp många konsulttimmar som support

Visa signatur

Processor: Motorola 68000 | Klockfrekvens: 7,09 Mhz (PAL) | Minne: 256 kB ROM / 512 kB RAM | Bussbredd: 24 bit | Joystick: Tac2 | Operativsystem: Amiga OS 1.3

Permalänk
Skrivet av Yellowcandy:

Grattis till första uppdraget! Får man fråga vart du satte dig prismässigt för 8 veckor / 320h arbete?

Tack så mycket!

Jag la mig på 500 SEK/timme - men med en engångsrabatt på 20 % som tack för att jag fick göra mitt examensarbete hos dem och som tack för att de var så nöjda med slutresultatet att de ville anlita mig denna gång - och jag har ett moraliskt dilemma: Hur ska jag göra om jag otroligt nog skulle bli klar tidigare än de 320 timmarna?

Ska det fasta priset tolkas som att uppdragsgivaren har köpt en "prisförsäkring" om att det aldrig kan bli dyrare än vad 320 timmar kostar även om jag kanske skulle behöva lägga mer tid på uppdraget för att nå överenskommen deadline? Har du någon inblick och/eller erfarenheter i det klassiska arbetsdilemmat?

Skrivet av talonmas:

Coolt hobbyprojekt och kul att du vill lära dig Wireshark. Men hos klienter bör du hålla dig till etablerade ramverk (react, angular etc) så får du mycket av säkerheten gratis. Klienten får också lättare att underhålla applikationen i framtiden.

O andra sidan, lyckas du låsa in dem till ditt egna ramverk så har du säkrat upp många konsulttimmar som support

Självfallet kommer jag inte att använda eget obeprövat ramverk hos klienter för det tycker jag säger sig lite självt. Däremot är det kul att lära sig och försöka återskapa mycket av vad som redan finns gratis inom sanerings-, validerings- och behörighetsvärlden ute på webben!

Vad jag är mest intresserad över i ramverket är hur det sår sig mot typiska OOP-ramverk: snabbare eller segare?

Wireshark vill jag dock enbart lära mig grundläggande för att bara förstå vilka dussintals olika protokoll som "härjar" där ute på webbnätverket genom att "nosa" på dem. Samt vill jag kolla vad exakt som syns (och inte) i diverse olika slags nätverkspaket för att förstå vad jag kan göra på utvecklarsidan.

T.e.x., vilka huvuden i HTTPS är (inte) osynliga tack vare krypteringen? Exempelvis upptäckte jag att CSP-huvudet löste alla mina tester med inline XSS i HTML-element men om det skulle bara kunna plockas bort innan resten av HTTPS-payload når en given slutanvändare så blir den ju rätt så "värdelös" att ens ha med. Hm!

Mvh,
WKF.

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

500kr + 20% rabatt låter som en otroligt bra deal för kunden!

Finns fördelar och nackdelar med alla varianter av hur man ska ta betalt men har du gjort liknande projekt i liknande storlek för under 300 timmar så hade jag sålt det för fastpris. Så undviker man sura diskussioner om du sitter och rullar tummarna eller om det skulle behövas 16 timmar till. Hos trogna kunder med större budget så brukade jag säljas ordinariepris under esterimering och sen halva priset över estimeringen men det är nog bara en deal man vill göra när det finns budget och förtroende för varandra.

Liten varning för sälja vid fastpris: skriv en kravlista innan du börjar jobba på ALLT som ska inkluderas, väldigt lätt att råka bjuda på massa funktioner som kunden tycker är självklar men du inte har estimerat.

Permalänk
Medlem
Skrivet av WebbkodsFrilansaren:

Tack så mycket!

Jag la mig på 500 SEK/timme - men med en engångsrabatt på 20 % som tack för att jag fick göra mitt examensarbete hos dem och som tack för att de var så nöjda med slutresultatet att de ville anlita mig denna gång - och jag har ett moraliskt dilemma: Hur ska jag göra om jag otroligt nog skulle bli klar tidigare än de 320 timmarna?

Ska det fasta priset tolkas som att uppdragsgivaren har köpt en "prisförsäkring" om att det aldrig kan bli dyrare än vad 320 timmar kostar även om jag kanske skulle behöva lägga mer tid på uppdraget för att nå överenskommen deadline? Har du någon inblick och/eller erfarenheter i det klassiska arbetsdilemmat?

Självfallet kommer jag inte att använda eget obeprövat ramverk hos klienter för det tycker jag säger sig lite självt. Däremot är det kul att lära sig och försöka återskapa mycket av vad som redan finns gratis inom sanerings-, validerings- och behörighetsvärlden ute på webben!

Vad jag är mest intresserad över i ramverket är hur det sår sig mot typiska OOP-ramverk: snabbare eller segare?

Wireshark vill jag dock enbart lära mig grundläggande för att bara förstå vilka dussintals olika protokoll som "härjar" där ute på webbnätverket genom att "nosa" på dem. Samt vill jag kolla vad exakt som syns (och inte) i diverse olika slags nätverkspaket för att förstå vad jag kan göra på utvecklarsidan.

T.e.x., vilka huvuden i HTTPS är (inte) osynliga tack vare krypteringen? Exempelvis upptäckte jag att CSP-huvudet löste alla mina tester med inline XSS i HTML-element men om det skulle bara kunna plockas bort innan resten av HTTPS-payload når en given slutanvändare så blir den ju rätt så "värdelös" att ens ha med. Hm!

Mvh,
WKF.

Det är extremt svårt att vara ense om vad som är ’klart’ eller ’bra nog’ eller ’enligt överenskommelse’

Vad är leveransen? Datum? Kvalitet, och hur den ska mätas? Vilka funktioner ska finnas, exakt hur ska de fungera och se ut? Har ni analyserat UX och liknande? Vem är användaren, vad har den för usecase(s)?

Att specificera vad som ska levereras är halva jobbet på ett projekt, och något som vanligen ändras under tidens gång

Är man klar för tidigt så kan man börja leta nya uppdrag eller om man är dumsnäll fråga kunden efter tillägg/förbättringar att göra

Permalänk
Medlem
Skrivet av WebbkodsFrilansaren:

Tack så mycket!

Jag la mig på 500 SEK/timme - men med en engångsrabatt på 20 % som tack för att jag fick göra mitt examensarbete hos dem och som tack för att de var så nöjda med slutresultatet att de ville anlita mig denna gång - och jag har ett moraliskt dilemma: Hur ska jag göra om jag otroligt nog skulle bli klar tidigare än de 320 timmarna?

Ska det fasta priset tolkas som att uppdragsgivaren har köpt en "prisförsäkring" om att det aldrig kan bli dyrare än vad 320 timmar kostar även om jag kanske skulle behöva lägga mer tid på uppdraget för att nå överenskommen deadline? Har du någon inblick och/eller erfarenheter i det klassiska arbetsdilemmat?

Självfallet kommer jag inte att använda eget obeprövat ramverk hos klienter för det tycker jag säger sig lite självt. Däremot är det kul att lära sig och försöka återskapa mycket av vad som redan finns gratis inom sanerings-, validerings- och behörighetsvärlden ute på webben!

Vad jag är mest intresserad över i ramverket är hur det sår sig mot typiska OOP-ramverk: snabbare eller segare?

Wireshark vill jag dock enbart lära mig grundläggande för att bara förstå vilka dussintals olika protokoll som "härjar" där ute på webbnätverket genom att "nosa" på dem. Samt vill jag kolla vad exakt som syns (och inte) i diverse olika slags nätverkspaket för att förstå vad jag kan göra på utvecklarsidan.

T.e.x., vilka huvuden i HTTPS är (inte) osynliga tack vare krypteringen? Exempelvis upptäckte jag att CSP-huvudet löste alla mina tester med inline XSS i HTML-element men om det skulle bara kunna plockas bort innan resten av HTTPS-payload når en given slutanvändare så blir den ju rätt så "värdelös" att ens ha med. Hm!

Mvh,
WKF.

Du kommer bli en grymt bra utvecklare. Drivet att alltid lära sig mer är en gemensam nämnare hos de jag mött i yrkeslivet och har kännt "fan va grym han är på det här". Önskar dig all lycka (en lycka du jobbar för att få så bara rättvist)!

Visa signatur

Processor: Motorola 68000 | Klockfrekvens: 7,09 Mhz (PAL) | Minne: 256 kB ROM / 512 kB RAM | Bussbredd: 24 bit | Joystick: Tac2 | Operativsystem: Amiga OS 1.3

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Förra veckan hade jag ett avstämningsmöte med en uppdragsgivare (en tidigare exjobbgivare) - samt fakturerade den första fakturan av tre enligt påskrivet offertavtal - där vi gick igenom de första huvudsakliga webbsidorna som ska tas fram i uppdraget. Det är som tidigare gjort diverse administrativa webbsidor för att kunna administrera diverse saker och ting inom uppdragsgivarens kärnverksamhet.

Nu till veckan kommer jag att för allra första gången att träffa uppdragsgivaren fysiskt och öga mot öga där vi då kommer att sätta oss ned i lugn och ro och gå igenom en fysisk produkt vilket är tänkt att få en digital modul vilket är där jag kommer in bilden med mitt webbutvecklande uppdrag på cirka 8 arbetsveckor. På tal om 8 arbetsveckor så börjar dessa egentligen imorgon (måndagen den 8:e juli 2024) om någon gång i början på september 2024 är leveransdagen.

I och med den digitala modulen jag ska ta fram så har jag för tillfället siktat in mig på att använda klassisk vanilla PHP & MySQL för backend medan frontend blir en kombination av vanilla PHP & vanilla JS för att göra vissa delar mer SPA-aktiga. Exempelvis så ska du kunna göra vissa CRUD-saker utan att webbsidan ska laddas om utan istället sker klassisk "state management" av data. Exempelvis att du tar bort en rad från en databas så sker även en ändring på frontend-sidan om den får ett "OK" från backend - fast detta sköts utan något särskilt JS-bibliotek.

En sak jag löst i mitt huvud nu och tack vare hjälp från gratisversionen av LLM-Gemini är situationerna där obegränsat antal av $_POST['variabel1'], $_POST['variabel2'], $_POST['variabel3'], $_POST['variabelOSV'] kan förekomma.

Nu kan jag inte bara återanvända funktioner från exjobbet utan även bygga vidare på dessa så att de kan använda sig av Regex som kontrollerar om du ska kolla efter $_POST['variabelX'], dvs., /^variabel\d+$/ efter att du har kontrollerat efter icke Regex-strängar av tillåtna $_POST-element.

Tanken är att fortfarande vara strikt så inget i inuti Regex. Faktum är att det kommer att gå att mata in egen skräddarsydd Regex beroende på vad för slags nummerkronologisk $_POST['data0123456789'] som strikt ska få ingå och inte.

Så nu till veckan blir det:
- Fixa färdigt ett par undersidor för särskild CRUD utifrån överenskommen kravspecifikation (i det senaste avstämningsmötet medgav uppdragsgivaren att de är medvetna om att vissa Utvecklare upplever missnöje med plötsliga förändringar i kravspecifikationer men jag är samtidigt flexibel i viss rimlig utsträckning)
- Träffa uppdragsgivaren IRL för att få sätta mig in i deras kärnverksamhets faktiska fysiska inslag i praktiken
- Fortsätta med uppdraget utifrån ytterligare överenskomna kravspecifikationer på ett delvis "agil-ish" vis

Hobbyprojekt
I takt med att jag arbetar med uppdraget så ger det även mig idéer för mitt hobbyprojekt som gäller det 99 % funktionsbaserade PHP-ramverket. Exempelvis har jag insett att:

['GET/test/{id}' => ['text/html', 'all']]

har frustrerande inslag av att du måste skriva in 'all' för alla undersidor som inte ska kräva någon form av autentisering såväl som tvärtom. Detta blir ohållbart om du ska ha hundra- eller till och med tiotusentals rutter. Således insåg jag att jag kan ha en $variabel som innehåller information om vilka rutter - beroende på vad de börjar med via str_starts_with() - ska ha för krav på autentisering. Det blir alltså en "Middleware Variable" som nyttjas när den rådande rutten granskas utifrån erhållen $_SERVER['REQUEST_URI'].

Så då kan jag ha att alla rutter som startar med exempelvis "/admin/", det vill säga:

if(str_starts_with($_SERVER['REQUEST_URI'],"/admin"))

ska alltid kräva autentisering. Alternativt kan den kontrollera mot de statiskt inskrivna rutterna i själva ruttvariabeln som då innehåller:"GET/admin" för då kan vi även göra det ännu mer finkornigt med val av HTTPS-metod och inte bara rutt.

I funktionsflödet då där autentisering ska äga rum så kan den använda sig av erhållen REQUEST_URI och bara kontrollera om den matchar någon i $MiddlewareVariabeln för att veta vad för slags autentisering som gäller och därmed ska kontrolleras mot. Strikt här vore att alla rutter alltid måste ha någon förvald typ av autentisering även om det är 'all' (inget autentiseringskrav alls). Då minskar det möjligtvis risken för en oönskad bugg genom att något råkade gå igenom. Givetvis finns fortfarande risken för stavfel i de faktiska rutterna som kan råka ge obehörig åtkomst.

Vidareutbildning
Vad gäller vidareutbildningen på egen hand så slog det mig för några dagar sedan att nu när distansutbildningen är över och den virtuella klassen har mer eller mindre upplöst (i alla fall jag som självmant valt att 'knippa den digitala strängen' från Discord-gruppen) så måste jag ändå säga att det ibland kan kännas lite ensamt att vara enskild frilanskonsult då jag i nuvarande uppdrag inte har någon annan tillräckligt IT-insatt att bolla idéer med eller bara det faktiska sociala umgänget.

Det är samtidigt en konsekvens som följd av att vilja arbeta 100 % hemifrån. Det hade dock varit roligt att hitta någon Discord där det går att hänga i samma röstkanal som andra enskilda frilanskonsulter. Men då har vi det där med sekretessavtal och dylikt som kan krångla till i vilken utsträckning faktiskt socialt umgänge kan äga rum för att inte tala om eventuella professionella kompetensutbyten. För mig är att hänga i en Discord-röstkanal lite som att sitta på samma arbetsplats vid jobb eller i samma klassrum vid studier.

Jag är fortfarande kvar på Transportlagret inom Nätverksteknikboken som jag håller på att gå igenom för att förstå mig på hur de olika nätverksprotokollen inom olika "lager" kan påverka det jag gör på Webbutvecklingssidan och hur jag kan skapa säkrare webbplatser genom att förstå lite mer ingående hur de olika nätverksprotokollen faktiskt fungerar och körs nere på nätverkspaketnivå.

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Idag efter ett härligt gympass sedan senast i april i år och ett felaktigt angivet kollinummer hos Posten efteråt så gjorde jag färdigt en undersida åt uppdragsgivaren. Tack vare det tidigare exjobbet så gick det att återanvända stora delar av koden med vissa justeringar här och var.

Samtidigt förstår jag mer nu varför ramverk dök upp såväl som hela tänket med MVC för att förhoppningsvis underlätta utvecklingen av undersidor (Views) medan all data (model) oftast är relativt lika med minimala förändringar via logiken (controllers). Jag har ett par funktioner (heh) som underlättar skapandet och valideringen av formulär för mig då detta uppdrag är relativt så formulärtungt trots att det är vanilla PHP, vanilla JS och Tailwind CSS.

Exempelvis har jag valideringsfunktioner såsom "validateString()", "validateEmail()", och "validatePhone()" där jag inte bara ger lägsta (där lägsta kan vara 0 vilket betyder att inmatningen är valfri) och högsta teckenlängd utan även vilken variabel som ska ändras om felmeddelande måste visas, samt vilken boolean som blir sann om allt har validerats korrekt, det vill säga att inget har invaliderats och därmed satt samma boolean till falskt.

Jag har mejlat uppdragsgivaren om dagens uppdatering, mina eventuella önskemål om att kunna leverera ett ännu bättre slutresultat samt någon allmän fråga om hur de önskar sig då jag gjort på ett sätt men de kanske önskar att ha det på ett annat sätt. Imorgon blir det att fortsätta med nästa CRUD-bitar för detta uppdrag.

En intressant utmaning är datamodelleringen då det delvis handlar om personer som ska kunna göra olika val i en nummerkronologisk ordning och då behöver jag databastabeller som håller reda på detta samt att inte råka ha situationen där två olika personer råkar (Update,Delete) UD:a mot samma databastabellrad för då kan det bli motsägelser i databasen.

I och med det hela med datamodelleringen och hur det kommer att CRUD:as mot i skarpt läge så har jag två varianter inlagda i databasen för närvarande:

1) Ett par icke-normaliserade tabeller som har nästan allt på samma plats
2) Flera normaliserade tabeller som använder främmande nycklar

Det "jobbigaste" utan att använda sig av något standalone ORM-PHP-paket är just att sammankoppla data via relationer. Exempelvis hämta alla användare och sedan koppla samman alla olika främmande nycklars data (dvs., data från andra tabeller kopplade till dessa användare) med dessa per användare. Det är/blir en hel del data parsing vilket kanske är vad Advent of Code var menad att hjälpa en med på sikt?! 🤔

Hobbyprojekt
Jag har inte ännu implementerat Regex-kontroll i min AllowedPOSTandGET-funktion vilket kommer göra det mer flexibelt såsom att tillåta upprepningar av liknande variabelnamn som exempelvis "user1" och "user2" genom att matcha det mot "/^user\d+$/" (nu börjar förstå lite bättre om Regex här. Notera att jag vill ha exakt matchning därav användningen av ^ och $ för att markera start respektive slut i matchningen. Jag skippar "i" för det ska vara exakt bokstavsstorlek. Snart börjar jag väl också drömma om Regex! 😂

En rolig sak med rutter är att du inte bara behöver kontrollera rutter mot klassiska webb(under)sidor utan också mot saker och ting som är av andra innehållstyper än text/html som exempelvis JSON samt bildfiler av olika slag. När jag hade min examensredovisning så hade jag fixat på min privata domän att bilder endast fick hämtas efter inloggning med rätt särskilt $_SESSION-nyckelparvärde. Så där fick jag lära mig hur det fungerar med att i princip nästan "lösenordsskydda" diverse filer online.

Nästa steg blir väl att bemästra application/octet-stream?! Det här med att strömma data kan ju vara farligt eftersom du skulle kunna skicka mer data än vad mottagaren förväntar sig och då orsaka minnesöversvämning (buffer overflow) så att arbiträr kod skulle kunna köras (ACE). Så därför undrar jag hur diverse webbservrar kan tillåta Transfer-Encoding som kan säga att det är "chunked" vilket betyder att Content-Length är okänd för ett givet nätverkspaket eller är hur det nu är?! 😕

Vidareutbildning
Jag var ej tillräckligt snabb på gröten eller bollen för att komma med i en av SweClockers kommande Shortcuts. Den jag hade gärna deltagit i är den med databaser då jag inte kan något om PostgreSQL men känner till det. Vad jag minns om det om jag minns rätt är att där går det att ha mer komplexa datatyper i databasen. Frågan är då hur det påverkar prestandan vid databassökning? 🤔

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Idag har jag rapporterat in till Uppdragsgivaren att det går nu att lägga till nytt i databasen samtidigt som det går att utläsa och jämföra mot tidigare inlagt i databasen. Detta löser då delvis problematiken med att öppna ny undersida som skapar ny enskild CSRF i databasen samtidigt som det förstnämnda är endast en tillfällig lösning.

En mer grundlig lösning blir senare att omarbeta databasen med CSRF då jag just nu har en databasstruktur med vissa upprepande använda kolumner som är definierade som unika vilket då kan skapa missnöje hos MySQL/MariaDB när de ska användas som främmande nycklar i diverse tabeller.

Vad Uppdragsgivaren nu kan göra dock är att när de ska lägga in nytt i databasen så kan de på samma undersida (istället för att öppna en ny som då skulle skapa en ny unik CSRF i databasen) via magin av PHP-baserat REST API och klientbaserad JS (ja, att ens behöva säga att det är JavaScript som körs klientsidan är ju lite sisådär med tanke på historiken med JS) välja tidigare data som de kan jämföra mot när de lägger in nytt. Detta tror jag kommer att uppskattas av Uppdragsgivaren.

Imorgon blir som tidigare sagt en IRL-träff för första gången sedan examensarbetet för ett par månader sedan. Då ska vi spela igenom något fysiskt spel som har en digital modul till sig. Jag kommer även under veckan att få nödvändiga data inlagd i databasen som kommer att användas för att testköra det hela att fungera på klientsidan som kommer att anropa mot ett REST API som endast fungerar vid inloggning och endast om den som anropar är från en särskild webbplats för det här med CORS och allt.

Nu var det väldigt länge sedan jag arbetade med JSON i PHP utan något ramverk som exempelvis Laravel, så detta:

$json = file_get_contents('php://input'); $data = json_decode($json, true); // ... Lots of stuff happening in-between here ... echo json_encode($data ?? "");

var lite ovant men det gick vägen. Det krävde en separat saneringsfunktion då min nuvarande endast behandlar $_POST- och $_GET-arrayvariablerna.

Även Laravel måste ju använda sig av ovanstående kod på det ena eller andra sättet. Allt i slutändan (förutom kanske deras egen lokala PHP-webbserver under utvecklingen(?)) är ju PHP i grunden.

Imorgon under IRL-träffen med Uppdragsgivaren hoppas jag att det hela kommer att kunna klarna mer i vad och hur de vill ha saker och ting.

Ja just det, det var en skum sak som jag inte förstod riktigt: på grund av att REST API:t kontrollerar om det är rätt slags användare inloggad innan REST API:t går vidare så kommer den att försöka skicka vidare en till inloggningssidan men på grund av att datatypen som returneras är application/json så såg jag bara 404 svarskod när jag provade samma ändpunkt i Thunderclient inuti VSCode.

Och detta orsakas endast av en funktion som körs då jag provade kommentera ur och in den. Ingenstans inuti den funktion kan jag se där svarskoden 404 sätts. Så vad jag misstänker är att PHP-servern lokalt har något standardsvar i svarskoden när den inte kan hitta rätt data att returnera i någon särskild situation. Den försöker returnera en undersida (då Location-huvudet skickas om man ej är inloggad eller inkorrekt inloggad) men i form av application/json vilket då borde ge 500? Hursomhelst, det skyddar iaf som det ska!

Hobbyprojekt
Som sagt tidigare så kan det jag gör i pågående Uppdrag även bidra till det 99 % funktionsbaserade PHP-ramverkshobbyprojektet då jag nu hittat vägar att sanera JSON-data erhållen från php://input. Inom någon snar(?) framtid kommer jag att dela med mig repot till det fria funktionsbaserade PHP-ramverket.

Det som utvecklas i hobbyprojektet handlar främst om rutter, databearbetning och mallmotorvyer så det här med kodupphovsrätt är inga problem i förhållande till vad som utvecklas i det pågående uppdraget för den som undrar. Faktum är att ett sådant kontrakt finns ej framtaget än så länge. I slutet av uppdraget kommer jag dock att genomföra ett juridiskt korrekt överlämnande till Uppdragsgivaren för det är inte mer än sedvanemässigt talat korrekt gjort.

Vidareutbildning
Jag kommer att ikväll kl.19:00 att delta vid Sweclockers Shortcuts 9/7: Databaser 19:00 där mitt användarnamn kommer att bli mitt tidigare "WebbKod Lärlingen" (på grund av ett GMAIL-konto) men i det hemliga underforumet kommer jag att ha kvar mitt nuvarande användarnamn.

Det blir spännande och roligt att få "snabbdjupdyka" ned i PostgreSQL då jag bara kan "vanlig" SQL än så länge. Det mesta ska vara relativt lika om jag förstått det rätt och det är just att det går att lagra mer komplexa datatyper i PostgreSQL-tabeller?

Förhoppningsvis kommer jag även att kunna bidra med ypperlig återkoppling så att dessa svenska utbildningstillfällen bara kan bli bättre och bättre. Kanske Sweclockers Shortcuts kommer att konkurrera ut alla distansbaserade YH-utbildningar inom tid nog?!

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Idag fick jag inte bara den första delbetalningen av tre i tid utan också tillfället att äntligen få träffa den numera Uppdragsgivaren (före detta exjobbgivare) öga mot öga, min hand skakad med den andres hand. Det var ett mycket givande möte och jag fick se det fysiska som ska få en digital modul och jag fick även reda på deras mycket intressanta nätverk där de berättade att de gärna kommer vilja att rekommendera mig för deras andra samarbetspartners.

Vad jag då ödmjukt sa - för det är inte mer än rätt - var att jag gärna skulle kunna tänka mig anlitas av andra i framtiden men då gärna med möjligheten med tillgång till en mentor för jag är fortfarande en "färsking" som junior-Webbutvecklare med fokus på fullstacksutveckling och mycket långt ifrån en senior.

Om jag däremot kan få samarbeta sida vid sida en senior så kan jag säkert påskynda mina nuvarande kunskaper och färdigheter snabbare än vad det går just nu. Vissa saker för mig är fortfarande lite i den "omedvetna inkompetensen", det vill säga, jag vet inte ännu vad jag ännu inte vet.

Tillbaka till Uppdraget: Jag kommer nu att få testdata att vidareutveckla den digitala modulen med. Ikväll fixade jag UPDATE-delen för en tabell för den administrativa delen (frontend + backend). Här kunde jag återanvända tidigare kod för UPDATE-delar för andra delar från samma administrativa del. Imorgon blir det READ-delen (frontend + backend) samt CREATE-delen (frontend + backend) för att kunna föra in testdata som sedan kommer att användas flitigt vid utvecklingen av den digitala modulen särskilda slutanvändare kommer att få ta del av vid lansering.

Det jag dessutom kan rapportera här och nu är att det kändes riktigt bra att få arvode betald i tid och det känns som om jag nu "respekteras professionellt på riktigt för mina kunskaper och min tid" då jag tidigare har arbetat för enbart 200 SEK/timme medan en annan nuvarande kund betalar 237 SEK/timme.

Förstnämnda har jag avslutat samarbetet med sedan länge tillbaka då "snubben" som driver det skeppet inte är klok på riktigt. Sistnämnda har jag fortfarande kontakt med - då det är en mycket vettig person - men det är ett uppehåll just nu under juli månad vilket är perfekt för då kan jag fokusera 100 % på nuvarande Uppdragsgivare.

Senare efter det mycket lyckade och givande mötet med Uppdragsgivaren så träffade jag en mycket god vän och vi kom på ett intressant uttryck ihop (finns säkert redan): "Du får de kontakter du förtjänar!" Tanken här är att om du beter dig som en idiot så kommer du antingen att:
1) få inga kontakter,
2) få kontakter men som varar väldigt kort då de snabbt upptäcker att du är en idiot, eller
3) idioter till kontakter för dessa tenderar att dras till varann då alla vettiga har hittat andra vettiga.

Om du däremot istället beter dig som en vettig person så kan du inte bara träffa andra vettiga personer utan också knyta an relationer med dem som kanske varar livet ut i bästa fall! 🫡

Idag var även en vilodag från gymmet men imorgon blir det gym. En annan mycket god vän kommer ej att samträna då så därför kommer jag att besöka gymmet tidigare än vanligt då jag föredrar mindre folk än mer folk.

Hobbyprojekt
Ingen uppdatering här denna gång. Mer eller mindre 100 % fokus på det nuvarande Uppdraget.

Vidareutbildning
Igår kväll och ikväll deltog jag i Sweclockers Shortcut. Jag har lämnat återkoppling kring detta i separat underforum. Eftersom det är "hemligt" än så länge så antar jag att återkoppling ej ska lämnas öppet här. Således inväntar jag bekräftelse på huruvida återkopplingen kring Sweclockers Shortcut får delas med i det öppna forumet eller ej.

Jag hoppas att den avancerade kursen om teckenkodning dyker upp för det hade varit roligt att äntligen(?) åtminstone få ett litet hum om hur dessa tecken som du läser just nu kan helt magiskt synas efter att massor av 1:or och 0:or i en särskild ordning från processorn till minnena till skärmen och/eller hårddisken har ägt rum.

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Det har gått framåt trots tystnad. På ett ungefär så ligger jag bra i fas i förhållande till de åtta överenskomna fakturerade veckorna i uppdraget. Jag har blivit bättre på att läsa, förstå och därmed även skriva SQL-satser särskilt när JOINs behövs. Samtidigt är det "premature-optimize"-nojan inom mig som undrar om JOIN är lämpligt eller om jag ska köra fler SELECTs istället vars data sedan slås samman på liknande vis. Men vissa saker kör jag JOINs på än så länge.

Jag har fått behöva lösa diverse saker som chatGPT4o (gratisvarianten där du har begränsat antal meddelanden per dag) och Copilot hjälpt mig med kodutkast där vissa varit bra och andra sämre. Oftast får jag ett kodutkast som jag ändrar smått eller så skriver jag lösningen som jag vill ha. Exempelvis så skulle jag databearbeta CSV-filer där varje rad har tre datapunkter, så i princip motsvarande Exceldokument med tre kolumner som börjar på A1, B1 och C1.

Jag skrev till LLM:erna att jag behövde kunna hantera tre olika samtidiga avskiljningstecken (eng. "delimiters") - ,;| - och då fattade den fel och föreslog att iterera igenom alla avskiljningstecknen för att hitta vilken som användes för en given CSV-rad. Men jag sa ju att det kunde vara olika avskiljningstecken samtidigt på samma givna CSV-rad?!

Så då sa jag åt den att istället lösa det med en regex som även kunde hantera alla möjliga olika slags bokstäver från det klassiska latinska alfabetet (åäö, kyrilliska, med flera):

if (preg_match('/^([\p{L}\s-]+)[,;|]([\p{L}\s-]+)[,;|](\S+@\S+\.\S+)$/u', $line, $matches)) { return \0 }

Du kan själv gissa vad det är den ska tugga igenom i denna regex ovan. Något som var mindre roligt att lösa men skönt när det väl fungerade var databearbetning av uppladdade Excel-filer (både XLS och XLSX). Först funderade jag på om jag bara kunde bearbeta med egen "minimal viable PHP code". Men det visade sig vara klurigt att tolka XLS(X)-data då de inte bara innehåller XML-data utan även varje cell kan ju vara specialformaterad i Excel-filen så då måste sådant tolkas.

När jag kikade i källkod från exempelvis PHPOffice/PHPExcel och dess uppföljare PHPOffice/PhpSpreadsheet så upptäckte jag fort att detta inte var något jag kunde göra på egen hand. Denna gång kunde jag inte återfinna Excel-tolkningshjulet på nytt under nuvarande återstående arbetstid. Så jag fick infinna mig i att använda Composer och PHPOffice/PhpSpreadsheet och hålla tummarna att det även fungerade på Loopias Apache-servrar med den senaste möjliga PHP-versionen där.

Komiskt nog så var det istället min XAMPP som behövde låsa upp några PHP Extensions (GD & ZIP Archive(?)). Först fattade jag inte varför jag behövde en PHP Extension för ZIP? Men så lärde jag mig den hårda vägen att Excel-filer är komprimerade specialiserade XML-filer. Dessa PHP Extensions fanns som tur var redan aktiverade hos Loopia så där var det bara att ladda upp vendor-mappen på lämplig plats och så fungerade det "direkt ur lådan" så att säga!

Dokumentationen för PhpSpreadsheet är dock rörig och har nog bristande "Hello World!"-exempel. Det finns exempel för att bara skriva till en given Excel-cell men inte för att bara läsa från en enstaka Excel-cell, eller jo, det finns men det är sjukt "gömt" för mig som är nybörjare. Du går till "Retrieving a cell value" i dokumentationen så får du veta exakt hur du hämtar en given cell i ett valt blad i en vald Excel-fil.

Det är också en hel del initiering för att komma igång med det:

  1. Du börjar med att skapa en "läsare" (IOFactory::createReader) och valfritt konfigurerar den där jag valde att endast kunna läsa data från Excel-filen.

  2. Sedan måste du ladda in filen till createReader-objektet.

  3. Därpå ska du välja vilket kalkylblad du vill CRUDa mot.

  4. Nu hämtar du det valda kalkylbladet du vill CRUDa mot.

  5. Nu i det valda aktiva kalkylbladet kan du använda getCell() och hämta värdet hämtar du det valda kalkylbladet du vill CRUDa mot.

Detta innebär följande förenklade kod:

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType); $reader->setReadDataOnly(true); $spreadsheet = $reader->load($inputFileName); $spreadsheet->setActiveSheetIndex(0); $sheet = $spreadsheet->getActiveSheet(); $row = 1; $getCell = $sheet->getCell('A' . $row)->getValue(); $row++;

Det finns en "hake" dock: Du kanske vill kolla först om det finns något värde i en cell innan du ens provar hämta från den. Men getValue()-metoden verkar inte fungera så bra här. Då finns istället getValueString() som kombinerat i en while-iterering kan kontrollera om givna celler i utvalda kolumner i Excel-filen är tomma strängar (dvs., tomma celler) och då hämta eller inte. Sedan sker det fler saker än enbart att hämta värden från en inläst Excel-fil, så klart!

I detta uppdrag så finns det administratörer och slutanvändare. Slutanvändarna kommer att behöva logga in och därmed använda sig av strikta HTTPS-säkrade sessionskakor. En utmaning och ett frågetecken här som jag troligen måste ta upp med Loopia som är leverantören för där webbplatsen är sjösatt är att hos Loopia så verkar ha blivit utloggad efter ett tag medan lokalt via XAMPP så sker det inte.

Följande sessionsinställningar används för närvarande:

ini_set('session.cache_limiter', 'public'); session_cache_limiter(false); ini_set('session.use_only_cookies', 1); ini_set('session.use_strict_mode', 1); ini_set('session.cache_expire', 30); ini_set('session.cookie_lifetime', 0); ini_set('session.name', 'rattmuffen'); ini_set('session.sid_length', 192); ini_set('session.sid_bits_per_character', 6);

Sedan när faktiska inloggningskakor skickas så sätts dessa på att leva i 8 timmar - en vanlig arbetsdag. Kruxet är att detta verkar sessionshanteringen på serversidan skita fullständigt i. Den verkar rensa upp sina sessionsfiler hos Loopia (där Redis används tror jag) tidigare än vad som sker lokalt vilket gör att om du bara står på tomgång på en undersida inloggad och kommer tillbaka efter ett tag så är du helt plötsligt utloggad fast din inloggningssessionskaka ska vara god för 8 timmar?! 🤔

Så här ser koden ut för en inloggningssessionskaka och anmärk hur lifetime då skriver över den första lifetime för en sessionskaka:

session_set_cookie_params([ 'lifetime' => 28800, 'path' => '/', 'domain' => 'RattMuffarnaAnfaller.nu', 'secure' => true, 'httponly' => true, 'samesite' => 'Strict' ]);

Ändå fungerar det inte hos Loopia utan där blir du utloggad om du bara står på tomgång på en undersida en viss tid. Detta väcker då Imposter Syndrome-känslor inom mig för här utvecklar jag en webbplats där vissa bitar av hur den körs på serversidan ändå verkar vara bortom min kontroll på grund av okunskap och möjligen ren och skär inkompetens i vissa fall.

Anmärk i inställningarna ovan att session.cookie_lifetime = 0 innebär tills webbläsaren stängs men detta skrivs över när en setcookie() faktiskt körs då den sätter 28800 (8 timmar) som livslängd. Sedan vet jag inte vad Cache Expire gör för något och om det kan vara relaterat vet jag inte. Jag läser här: https://www.php.net/manual/en/function.session-cache-expire.p... och här: https://www.php.net/manual/en/session.configuration.php#ini.s... men blir varken klokare av det. Sweclockers är jag ju inloggad på och kan stå på tomgång på undersidor utan att behöva logga in igen!?🙃

Det är en allvarlig problematik eftersom slutanvändarna ska kunna vara inloggade i flera timmar än att de helt plötsligt loggas ut på grund av något PHP Session Management-tjofs som jag inte har löst ännu på grund av för tillfället rådande inkompetens från min sida. Hm...😤

Förutom denna bit med sessionsfilhantering i PHP så går uppdraget galant. Jag är halvvägs mot mål vilket är någon i början på september månad i år. Jag kommer under denna vecka att få en Adobe XD-fil som ligger till grund för utseendet för den webbplats som slutanvändarna ska ta del av. Det blir spännande och samtidigt utmanande då jag saknar den där designkänslan. Men förhoppningsvis kommer jag att kunna följa prototypfilen så pixelperfekt som möjligt.

Hobbyprojekt
I takt med saker jag löst i det nuvarande pågående uppdraget så har jag insett flertals detaljer som kommer att behöva justeras i det 99 % funktionsbaserade PHP-ramverket. En lösning jag kommit på är dock att jag kan lagra i en variabel diverse metoder med rutter som mb_strtolower($_SERVER['REQUEST_URI']) kan börja med som ett sätt att sedan veta om alla rutter som börjar så för given HTTP-metod ska autentiseras och/eller auktoriseras, såväl som om de ska ha en standard innehållstyp - allt för att minimera upprepningar i variabeln som faktiskt innehåller alla de olika rutterna.

En sak att påpeka här är att hobbyprojektets delar inte används i det pågående uppdraget. Det finns alltså ingen ramverksliknande kod utan det är något som utvecklas på fritiden för jag vill ju inte sjösätta något otillräckligt otestat hos betalande slutkunder. Vem tror jag att jag är egentligen: CrowdStrike på en fredag under semestertider?! 😂

Vidareutbildning
Utöver deltagande i ett par av Sweclockers Shortcuts webbinarier så har jag även läst lite mer i Datornätverksboken där jag fortfarande är på Transportlagret. Det är rätt fascinerande och ändå rätt så otroligt hur TCP i samverkan med TLS kan skicka massor av paket för det finns ju någon begränsning också i hur stora vissa slags nätverkspaket får vara och så kan TCP ändå göra så att all paket kommer i rätt ordning så att data kan komma i rätt ordning och ej trasigt.

Sedan har nätverk sin egen terminologi av packets, segments, frames, med flera, att hålla reda på för att förstå vad som är inbakat i vad och vilka olika protokoll och/eller lager som skickar och/eller ta emot vad. På så vis kan jag därmed förstå hur det kan vara sin egen långa utbildning för att verkligen bemästra grunderna inom nätverkande samtidigt som nätverket är en rätt så viktig grej inom webbutveckling. Varför ska jag utveckla en webbplats som jag ändå inte får ut på "nätverken" hur litet eller stort det än må vara?

Nu ska jag steka två 90 grams hamburgare med ketchup och hushållsost på!🤤

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Med försiktig optimism kan jag gladeligen meddela att kodförändringen från:

ini_set('session.cache_limiter', 'public'); session_cache_limiter(false); ini_set('session.use_only_cookies', 1); ini_set('session.use_strict_mode', 1); ini_set('session.cache_expire', 30); ini_set('session.cookie_lifetime', 0); ini_set('session.name', 'rattmuffen'); ini_set('session.sid_length', 192); ini_set('session.sid_bits_per_character', 6);

till:

ini_set('session.cache_limiter', 'public'); session_cache_limiter(false); ini_set('session.use_only_cookies', 1); ini_set('session.use_strict_mode', 1); ini_set('session.cache_expire', 30); ini_set('session.cookie_lifetime', 604800); ini_set('session.name', 'rattmuff'); ini_set('session.sid_length', 192); ini_set('session.sid_bits_per_character', 6); ini_set('session.gc_divisor', 100); ini_set('session.gc_maxlifetime', 604800); ini_set('session.gc_probability', 0);

verkar ha lett till att en inte loggas helt plötsligt efter knappa någon halvtimme. Det jag gjorde var att föra in phpinfo(); i en PHP-fil som kördes lokalt och sedan online för att jämföra vilka initiala värden som sattes. Inte förvånande nog så såg jag att Redis ("Rediscluster") körs hos Loopia och då trodde jag först att jag var körd.

För hur ska jag konfigurera något som jag ej har åtkomst till? Det finns ingenting väl inloggad hos Loopia som låter en konfigurera Redis och/eller Rediscluster mer än vad som kanske hade gått med ini_set(?) Jag frågade ChatGPT4o (gratisvarianten) vad jag kunde göra och enligt den så skulle det gå att bara ändra diverse parametrar för session och Redis / Rediscluster skulle nyttja dessa trots att de även har sina egna konfigureringsparametrar som går att utläsa när phpinfo(); hos Loopia.

Jag provade då den nya uppsättningen av ini_set(); runt kl.17:00-tiden och drog sedan på bio för att se Deadpool 3 vilket var precis som väntat: köttig komedi vars översatta svordomsrepliker måste ha varit ett rent nöje för den svenska översättaren att kreativt få skriva svensk svordomspoesi av!

Sedan tog jag en promenad efter bion och införskaffade osötad ketchup och slängde ut Heinz förskräckliga ketchup som smakar inte bara socker utan också äckligt sötningsmedel. (Varför både socker och sötningsmedel i en och samma produkt? Okej om det är det ena eller andra?!) Så runt 22:00-tiden så startade jag arbetsdatorn och provade navigera på en inloggad sida hos Loopia där jag nu hoppades på att fortfarande vara inloggad...

... och inloggad var jag fortfarande denna gång!🤯

Nu kommer nästa test: är inloggad i skrivande stund och imorgon runt lunchtid kommer jag att kolla igen om jag fortfarande är inloggad online. Är jag inte det så blir det allvarligt samtal med Loopia om varför deras UNIX Apache-server med PHP 8.3 inte klarar av att låta sessionsfiler få överleva längre 12 timmar!😡

Förhoppningsvis har jag all makt via rätt parametrar införda ini_set(); som körs innan någon session startas i alla PHP-filer. Det har varit ett litet orosmoln då det sker saker bakom kulisserna inom PHP vilket jag inte riktigt förstår som "färsking" inom webbutvecklingsvärlden där min enda "seniora kodmentor" är i princip Google och LLM:er! 🤣

Så länge inloggningen kan vara så länge som jag satt den, dvs., att sessionsfilerna - via Redis eller annat - finns kvar så länge som de är satta att finnas kvar, så kommer resten att inte bli några större bekymmer. En intressant sak är om jag ska nyttja HTMX för att underlätta den stora mängd AJAX som kommer att nyttjas för en frontend-sida där inloggade slutanvändare ska få ta del av det den administrativa delen tillhandahåller dem med.

En härlig sak med AJAX-anrop är att sessionskakorna skickas med så länge det är samma Domain och sessionskakorna är konfigurerade att ta emot dem därifrån. Detta kan vi tacka RFC 6265 sektion 5.2.3 om Domain-attributet för HTTP-huvudet Set-Cookie för! Eller ja, också att de största webbläsarna också följer denna föreslagna webbstandard!

Så då blir det att hoppa i sängen och drömma om att jag vaknar upp och ser att jag fortfarande är inloggad online hos uppdragsgivarens webbplats cirka 12 timmar efter att jag hade loggat in.

Hobbyprojekt
Inget nytt att rapportera här mer än att alla viktiga problem lösta i detta uppdrag går att ta med till utvecklingen av det 99 % funktionsbaserade PHP-ramverket eftersom det är lösta universala problem som inte är tillräckligt unika men ändå så nödvändiga för allas användbara trevnad ute på den världsliga vida webben.

Vidareutbildning
Inget nytt att rapportera här mer än vad denna nyduschade kodhulk hade att säga om CrowdStrike's ofrivilliga automatiska incident:

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Idag runt kl.12:00-tiden så hade min försiktiga optimism uppfyllts: jag var fortfarande inloggad på Loopia på uppdragsgivarens webbplats vilket betyder att kodförändringarna för sessionshanteringen på servern via Redis & Rediscluster fungerade. Det känns och är bra!

Nästa steg är nu avstämningsmöte varpå efteråt start för frontend-delen där slutanvändarna ska få ta del av det administratörerna tillhandahåller från annat håll. Här blir det en hel del AJAX och tankarna går kring HTMX eller något eget förenklat med nödvändig DOM-manipulering på klientsidan. Självfallet kommer ett robust API finnas som har mycket strikta parametrar gällande ändpunkter och datainnehåll.

En rolig sak jag även gjorde idag var att jag skrev en SQL-fråga med hela tre JOINs. Förhoppningsvis skulle min före detta databaslärare ge tummen upp eller något i stil med: "Det går, men det är fel!" 😂 Klurigheterna är att först välja en tabell som du sedan slår ihop med en annan och sedan slår ihop den med nästa tabell och så vidare. Här gäller det att hålla ihop vilka som är främmande nycklar till vilka tabeller och så vidare.

Vad som återstår på SQL-vägar är på något "magiskt" vis kunna hämta tre rader vars främmande nyckel pekar på en främmande nyckel fast där alla tre rader nu istället blir tre nya kolumner för en rad. Anta att jag har tabellen Bil vars primärnyckel används som främmande nycklar i en tabell Däck där alltid tre däckrader pekar på samma enstaka Bil. Detta innebär i klassisk SQL-anda att du får tre rader som pekar på varje Bil när du kör JOIN på dem.

Men jag vill ju ha så att det istället skulle bli en rad i stil med: Bil, Däck1, Däck2, Däck3 och i Däck1-3 kolumnerna för denna Bil så är det exempelvis däcktryck eller något annat. Och datastrukturen och implementationen är som så att varje gång en Bil förs in i databas så är det alltid garanterat tre Däck som också förs in som pekar på denna Bil.

Eller så får jag bita i det digitala äpplet (jag gillar förresten sura äpplen - Granny Smith *MUMS*!) och helt enkelt för varje Bil loopa igenom de tre sammankopplade tre däcken som tillhör det och skapa nycklarna:

$car[0]['wheel1']; $car[0]['wheel2']; $car[0]['wheel3']; $car[1]['wheel1']; $car[1]['wheel2']; $car[1]['wheel3']; // And so on...

Jag har inte ännu lärt mig att loopa igenom samma tillhörande rader från en tabell vars primärnyckel är i princip främmande nycklar till en annan tabell. Någon måste ju göra det. Vare sig det är jag, Microsoft Entity Framework Core eller valfritt PHP ORM-system som exempelvis Doctrine. Sistnämnda verkar dock inte att använda självständigt utan det måste föras in i något valt PHP-ramverk *suck*.

Men även dessa "svarta kodlådor till ORM:s" är ju bara grundläggande kod i grunden. Så jag kan lika gärna lära mig klassisk databearbetning av erhållna databasdata och trycka in dessa i rätt nycklar för rätt erhållna huvudsakliga databasdata, alltså alltid rätt tre däck för varje rätt bil! 🫡

Jag börjar få lite mer "feeling" för JOINs nu då jag visualiserar hur jag i princip sätter fast en tabellcell i en tabellrad där det är främmande nyckeln från den andra tabellen som berättar i slutet på vilken tabellrad jag ska sätta den. Så länge ON blir rätt vid JOIN och rätt kolumner väljs så blir det som önskat.

Den lilla grejen som kom upp var att då id används som kolumnnamn för alla primärnycklar i alla tabeller i detta uppdrag så gäller det att dessa inte råkar skriva över varandra när tabeller slås samman. För vad som hände var att fel id från en tabell blev primärnyckel för när jag skulle länka "view-car.php?car=car['id']" så det blev fel. När jag ändrade vad som skulle ingå i SELECT för varje tabell så löste det sig!

Nästa steg är som sagt var nästkommande avstämningsmöte och vad som ska göras därifrån vilket är givetvis frontend-delen för slutanvändarna och på ett sätt enligt den rätt så flexibla kravspecifikationen.

Hobbyprojekt
Inget nytt att rapportera.

Vidareutbildning
Inget nytt att rapportera.

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Idag var avstämningsmöte med nytt inbokat fysiskt möte där den digitala modulen kommer att testköras i skarpt läge (i mitten på augusti i år), det vill säga, jag och uppdragsgivaren kommer att agera som försökskaniner i egenskap av slutanvändare. Glädjande idag var att jag nu har fått igång andra webbplatsen hos Loopia och den kan ansluta till databas utan några konstigheter - allt enligt erhållen information från Loopias kunskapsbank.

Så länge sessionshanteringen blir som tänkt - att slutanvändarna kan vara inloggade åtminstone ett dygn vilket bör fungera då jag fortfarande är inloggad på den administrativa webbplatsen som använder exakt samma ini_set()-konfigurering på alla undersidor - så blir resterande en intressant utmaning.

Kruxet är detta: Varje slutanvändare kommer att göra saker via AJAX och innan de gör något, när de gjort något, så ska en given databastabell hålla reda på vilken slutanvändaren som har gjort vad, vad den ska kunna göra härnäst, var någonstans slutanvändaren är i en given process vilket är själva kärnan i denna digitala modul, och det är den lilla databastabellen som jag måste designa rätt så blir implementeringen också busenkel!

Uppdragsgivaren önskar även statistik så vid varje handling från slutanvändarens sida ska även statistik samlas och sammanställas. Här har jag tre intressanta alternativ för att lösa det:

1) Lagra bara i databas för vad en slutanvändare har gjort.
2) Lagra bara i en Excel-fil för vad en slutanvändare har gjort.
3) Lagra i både ock.

Jag har ju lärt mig att läsa från Excel-filer och det bör inte vara något större krångel med att kunna skriva till en utvald Excel-fil. Viktigt här är att se till att denna Excel-fil inte går att komma åt online. Således bör ligga utanför public_html-mappen.

Att kunna skriva in i en given rad i en Excel-fil hade påskyndat sammanställningen för då går det sedan att ha någon formel(?) i samma Excel-fil som visuellt kan sammanställa vad olika slutanvändare har valt vid olika skeden inuti den digitala modulen. Och om du undrar så är dessa data anonyma då inga personuppgifter finns lagrade i Excel-filen.

Om Du är kunnig på Excel så får du gärna dela med dig tips på hur jag ska lagra data i Excel-filen för att enklare senare kunna visuellt sammanställa all data. För närvarande tänker jag att varje rad representerar en slutanvändare där varje kolumn sedan representerar vad denna slutanvändare har valt i olika skeden i den digitala modulen. Då bör det går att sammanställa procentuellt andelen av slutanvändare som har valt ditten och datten utifrån utvalda kolumner i Excel-filen.

Liknande skulle nog också gå i SQL för då får jag ju lära mig nyttja aggregeringsfunktioner(?) om jag minns rätt?🤔

Hobbyprojekt
Inget nytt att rapportera.

Vidareutbildning
Inget nytt att rapportera.

Trevlig Helg & På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk
Medlem

Spännande att läsa om hur det går för dig!

Jag skulle starkt avråda från att lagra data i en Excel-fil. Jag antar då att du menar .xslx eller dylikt, samt att du planerar att ”skriva lite i taget” (dvs, du har inte all data när du gör första skrivningen). Formatet är väldigt otympligt att jobba med programmatiskt, inte minst för att det under ytan är en zippad samling XML-filer. Därtill finns det en massa felhantering att tänka på när du jobbar med filbaserad lagring. Vad gör du om en skrivning misslyckas halvvägs igenom, exempelvis? Hur hanteras samtida (concurrent) anrop?

Nu vet jag inte exakt hur din situation ser ut, men jag kan med stor säkerhet säga att det är fel lösning att hantera ett .xslx-bibliotek som en databas. Lagra data i databasen. Om kund håller kniven mot strupen på dig att du ska leverera resultat i .xlsx, producera då det när all data finns tillgänglig i databasen.

Permalänk
Medlem

Tillägg till ovanstående, kanske är enklare att generera en csv fil (tänk på att svenska Excel vill ha semikolon istället för komma som avdelare i filen) av datan istället som beställaren sen kan öppna i Excel och använda och spara som en xlsx på sin lokala dator.

/Viktor

Permalänk
Skrivet av SimpLar:

Spännande att läsa om hur det går för dig!

Jag skulle starkt avråda från att lagra data i en Excel-fil. Jag antar då att du menar .xslx eller dylikt, samt att du planerar att ”skriva lite i taget” (dvs, du har inte all data när du gör första skrivningen). Formatet är väldigt otympligt att jobba med programmatiskt, inte minst för att det under ytan är en zippad samling XML-filer. Därtill finns det en massa felhantering att tänka på när du jobbar med filbaserad lagring. Vad gör du om en skrivning misslyckas halvvägs igenom, exempelvis? Hur hanteras samtida (concurrent) anrop?

Nu vet jag inte exakt hur din situation ser ut, men jag kan med stor säkerhet säga att det är fel lösning att hantera ett .xslx-bibliotek som en databas. Lagra data i databasen. Om kund håller kniven mot strupen på dig att du ska leverera resultat i .xlsx, producera då det när all data finns tillgänglig i databasen.

Skrivet av vg132:

Tillägg till ovanstående, kanske är enklare att generera en csv fil (tänk på att svenska Excel vill ha semikolon istället för komma som avdelare i filen) av datan istället som beställaren sen kan öppna i Excel och använda och spara som en xlsx på sin lokala dator.

/Viktor

Tjo igen! Tack så mycket för tipsen!

Ja, det är ju därför så kallade databaser finns till! Det jag tänker mig då för att förbereda att kunna föra in i någon mjukvara som kan visuellt sammanställa erhållna data är då följande databastabell som ska hålla reda på slutanvändare, sammanhang, samt rader, kolumner och deras värden i en Excel-fil:

CREATE stats( id INT PRIMARY KEY AUTO_INCREMENT, UserId VARCHAR(96) NOT NULL, FOREIGN KEY (UserId) REFERENCES users(UserId) ON DELETE CASCADE, CtxId INT NOT NULL, FOREIGN KEY (CtxId) REFERENCES contexts(id) ON DELETE CASCADE, ExcelRow INT NOT NULL, ExcelColumn VARCHAR(10) NOT NULL, ExcelValue VARCHAR(255) NOT NULL, );

UserId är alltså slutanvändaren och så har vi även ett sammanhang (CtxId) vilket betyder att det kan finnas olika sammanställningar för samma slutanvändare. Sedan ska ExcelRow tillsammans med ExcelColumn peka ut A1, B1, A2, B2, osv. medan ExcelValue är vad som ska lagras i den valda Excel-cellen i en given Excel-fil. Låter det som en OK lösning eller någon "gotcha!" här?🤔

Mvh,
WKF.

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk
Medlem

Tänk på att namnge kolumnerna i databasen efter vad de innehåller, inte var de kommer att hamna vid en eventuell export av datan. I framtiden kanske du ska exportera datan till ett annat system eller till ett annat filformat då säger Excel inte så mycket som namn på kolumnerna.

/Viktor

Permalänk
Medlem

Instämmer med ovanstående. Lagra datan på ett sätt som är vettigt för datan, inte för en i förväg uttänkt slutdestination. Du kan som nämnt vilja exportera till andra system, eller kanske bara vill ändra layout i Excel-exporten. Vad vinner du på att beräkna kolumn och rad i förväg? Den enda anledningen jag kan se till att göra beräkningar och aggregat i förväg är prestanda eller för audit, men jag får inte känslan av att det är så stora datamängder eller strikt reglering det handlar om här?

Notera också att de flesta databassystem stödjer att exportera en SELECT direkt till CSV, som @vg132 redan nämnt att Excel kan läsa.

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Sedan senast rapporterat så har jag nu fått till frontend-delen för slutanvändarna i den digitala modulen. När de har lagts till av administratörerna så kan de sätta och/eller ändra sitt lösenord genom att ange e-postadress som först kontrolleras att det ens överhuvudtaget finns med i databasen.

Sedan mejlas de en återställningslänk som är IP-kopplad och gäller endast i 10 minuter från och med utfärdandet av mejlet med återställningslänken. Mejllösningen skedde med hjälp av PHPMailer i kombination med konfigureringskod från Loopia. Var inga större problem. Det enda var att det ej gick att testköra det i localhost vilket är förståeligt!

Nästa steg var att låta de logga in och använda egen sessionshantering än vad administratörerna gör på en helt annan webbplats. Inga problem! Sedan var det att fixa så att varje inloggad slutanvändare kan se om de har åtkomst till att delta i den digitala modulen vilket är indelad i tre omgångar:

- Första omgången gäller under bestämda datum från administratörerna,
- Andra omgången under andra bestämda datum och,
- Tredje omgången under andra bestämda datum.

Här måste även specialfallet hanteras där det kan vara uppehåll mellan någon omgång vilket betyder att det finns datum mellan bestämda datum som inte heller ska gå att komma åt för en given omgång har inte börjat ännu då.

Så först kontrolleras om det finns något för slutanvändarna att delta i överhuvudtaget, dvs., har första omgången ens startat - har de ens några omgångar kopplade till sig? Sedan kontrolleras vilken omgång och nu är jag i steget att hålla koll på var någonstans de är i under en given omgång. Omgångarna har samma egenskaper: start- och slutdatum, saker de gör via AJAX på sina mobila enheter, och samlad poäng.

Då varje slutanvändare är unikt kopplat till en uppsättning av alltid tre omgångar och varje användare har unikt så får jag en tabell att koppla slutanvändare och uppsättningen de tre omgångarna och då kan den tabellen innehålla saker som vilken nuvarande omgång de är i, nuvarande "aktivitet" i den nuvarande omgången, totalpoängen, och ett par saker till.

Egentligen är det ovannämnda inte det kluriga utan återigen så är det specialfallen att hantera: Då det är AJAX, vad händer om slutanvändaren får för sig att "hårduppdatera" PHP-undersidan vilket då även laddar om AJAX-skriptet som talar med ett API? Ett annat specialfall är att en "aktivitet" innehåller tre steg: du gör en sak (ett AJAX-anrop) och sedan startar en nedräkningsklocka (ett annat AJAX-anrop) och sedan gör du en till sak (ett tredje AJAX-anrop). Vad händer då om de "hårduppdaterar" PHP-undersidan under nedräkningsklockans tid?🤔

Då måste jag även hålla koll på när den började och när den ska sluta så om de får för sig att "hårduppdatera" PHP-undersidan. Det är just dessa små specialfall att hantera som är den stora utmaningen, inte när vi utgår från att de bara gör rätt från början och under hela processen i den digitala modulen!

Men det är ju när någonting inte fungerar som vi gärna vill yttra oss om det och dessutom utvecklar jag ju något som är tänkt att säljas för riksdaler så då måste det vara så buggfritt som det bara går vare sig beteendet är ofrivilligt (råkade skrolla högst upp i mobilen och sidan laddas om) eller frivilligt (obetald/nyfiken "pen-tester", hähä). Samtidigt så kommer den digitala modulen att säljas under avtal där det finns klausul om "produktmissbruk" med vite som följd.

En annan "knorr" i det hela är att i samband med "deltagandet" i den digitala modulen för varje enskild slutanvändare så ska statistik lagras för att kunna sammanställas på önskat vis. Så då tänker jag en tabell som innehåller vilken samling omgångar, vilken slutanvändare och sedan kan två övriga kolumner vara vilken typ av data som det gäller och sedan datavärdet. På så vis blir det framtida kolumner med data om det nu ska sammanställas i något slag.

Det nästan "magiska" som är implementerat här då är en tabell som växer på höjden (radantal) och inte bredden (kolumnantal) samtidigt som den samlar in data för att vid senare tillfälle skapa tabeller med olika "bredder" (kolumnantal).

Då kommer vi till själva AJAX- & API-delen: Hur är denna då på ett ungefär uppbyggd?🤔 Å ena sidan vill jag förhindra massa oönskade API-anrop där alldeles för nyfikna obetalda "pen-testers" vill "fuzzy-anropa" allt möjligt. Nu är det så att detta PHP-baserade API inte hanterar URI-ändpunkter men jag måste ju på något vis "veta" vem (slutanvändare), vad och i vilket skede (skickar de med data, klickade de på något i den digitala modulen, och i vilken omgång skedde det, eller är de fortfarande under nedräkningsklockans järngrepp?).

Först så bestämde jag mig för slumpvisa URIs vilket bara innehåller "trams" men på API-sidan betyder något:

$allowedURIS =["O-PDIFGJp!sDofiGje3495?yudg_ipg-m" => "currentUserStatus"];

(Nej, ovanstående obfuskerad "URI" finns inte med i den faktiska arrayvariabeln!)

Då skickas denna konstiga sträng med som "uri" i ett POST-anrop via fetch(). Den associativa arrayens nycklar är alltså URI som står för en funktion som ska köras inuti API:t. Men! Detta betyder att efter tag så går det att se på ett ungefär vad de olika obfuskerade URIs gör för något om jobbigare att reda ut och det finns inget mönster som då berättar om hur många faktiska URIs som kanske API:t kan hantera. (Ja, API:t kontrollerar också så att det är rätt slutanvändare och att de faktiskt kan ta del av de olika omgångarna utifrån bestämda datum och så vidare, så skulle någon försöka anropa API-filen direkt så kommer de inte vidare för de saknar troligen inloggningssession till att börja med...)

Nedanstående visar sedan kortfattat hur vald obfuskerad "URI" anropar funktion vilket i sin tur använder databasanslutning för att sedan lagra erhållen HTML-data och kanske annat vilket sedan skickas tillbaka och skulle $res otroligt nog vara null så skickas då felmeddelande enbart:

$res = null; $functionToCall = $allowedURIS[$data['uri']]; if (function_exists($functionToCall) && is_callable($functionToCall)) { $res = $functionToCall($data, $db); } function currentUserStatus($data, $mysqli) { return [ "rattMuff" => "<div class='flex items-center justify-center h-[80dvh] w-full'><p class='text-center text-white text-2xl font-OpenSansRegular'>Detta är ett paragraftest som hämtades från \"servern\" och laddades sedan dynamiskt fram hos slutanvändaren!</p></div>", ]; } header('Content-Type: application/json'); http_response_code(200); echo json_encode($res ?? ["error" => "( ͡° ͜ʖ ͡°)"]); exit();

Sedan kom jag på att om jag vet vilken slutanvändare det är och de har pågående "omgångar" i den digitala modulen så bör jag via en tabell som ska hålla reda på var de är någonstans utan att behöva använda olika obfuskerade URI:er. Så det blir nog den lösningen. Genom att veta var de är någonstans i processen så kan diverse funktioner ändå köras för de kontrollerar ändå så att rätt eventuellt "interaktionsdata" finns med beroende på var någonstans i processen slutanvändaren ifråga är.

En tredje lösning vilket troligen inte kommer att implementeras alls men roligt att jag tänkte på den var att köra inspiration från "Double Ratchet Algorithm" där varje anrop då skickar med en ny unik URI som endast slutanvändaren ifråga kan använda och som då kommer kontrolleras i databasen vid varje anrop. Dock misstänker jag mer huvudvärk i form av fler specialfall att behöva hantera som följd. Men rolig tanke annars! Jag har för mig att liknande implementering finns för Access Tokens (AT) där en ny AT skickas efter ett tidigare lyckat anrop med tidigare tillhandahållet AT?🤔

Förmodligen blir det minst jobbigaste obfuskerade URIs som ovan och/eller att bara ha stenkoll utifrån något lagrat i sessionsvariabeln när sessionskakan med samma id skickas med vid varje anrop. En härlig sak då är att det är busenkelt att prova så att sessionskakan fungerar som den ska: ThunderClient och POSTMAN i VSCode loggar aldrig in så jag skickar alltid anrop utan några kakor så då får jag bara tillbaka 404 - precis som tänkt!🫡

Hobbyprojekt
Inget nytt att rapportera.

Vidareutbildning
Inget nytt att rapportera.

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk
Medlem

Kan inte mycket om php men gissar att du har någon typ av sessionshantering även i php och det känns som att mycket av dina frågor/problem kan hanteras/lösas med hjälp av det. När du gör dina ajax anrop till servern så sparar du ner information i användarens session så att om användaren laddar om sidan så vet du var den befinner sig genom att läsa session informationen. T.ex. om du har flera steg som användaren ska gå igenom och varje stegbyte är ett ajax anrop, spara ner i session vilket steg användaren är på och om de laddar om så kan du bara återställa sidan så att den blir så som den ska vara vid frågan den fråga som är sparad i session.

Samma sak för ditt API, du kan ju i session kolla om användaren är inloggad och om den är i ett "state" där den får göra ett API anrop eller inte. "Security through obscurity" är inte alltid den bästa eller lättaste vägen

Bra att tänka på säkerhet när det gäller återställning av lösenord men tänk också på att just IP lås kan vara lite problematiskt när många jobbar med olika datorer, VPN, telefoner etc som kan ha olika IP'n. Du får kanske mailet till en annan enhet än den som du gör beställningen på. Men är det bara för admin användare kan det något de får leva med

/Viktor

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
I skrivande stund befinner jag mig på annan ort med bärbar arbetsdator så arbetet fortsätter precis som vanligt. Igår hade jag ett avstämningsmöte med Uppdragsgivaren som då fick ta del av prototypen för frontend-delen. Och innan det ägde rum så hade jag ett riktigt "Kodmaraton" för att få till ett relativt hyfsat "utkast" av frontend-delen som samspelar med ett PHP-baserat API.

Nedanstående är frontend-delens JavaScript-kod vilket innehåller ett antal console.log(s) vilket kommer att försvinna vid lansering (uppenbara data har obfuskerats):

// Run script after DOM is loaded document.addEventListener("DOMContentLoaded", function () { // Grab DIV that will be used for the API AJAX based game! gameDiv = document.getElementById("game"); // Fetch using localhost or online version const fetchURI = location.hostname === "localhost" || location.hostname === "127.0.0.1" ? `LOCAL_URL` : `ONLINE_URL`; // Start listening for clicks inside of it when it exists! if (gameDiv != null) { gameDiv.addEventListener("click", function (e) { // Check if we clicked on a button element if (e.target.tagName === "BUTTON") { // Then check for "data-dataToSendBack" attribute if (e.target.hasAttribute("data-dataToSendBack")) { // Extract its string data const prepareDataToSendBack = e.target.getAttribute( "data-dataToSendBack" ); console.log(prepareDataToSendBack); // Store data as a just a string or as an array let prepData = prepareDataToSendBack.includes(",") ? prepareDataToSendBack.split(",") : prepareDataToSendBack; console.log(prepData); // Now we extract data from form based on what kind of input elements // which we know by splitting the prepData (array or string) based on ":" // which separates the input element type from the input element name // For example "r:inputName" (radio buttons) or "c:inputName" (checkboxes) // or "s:inputName" (input text field) or "b:inputName" (button). // We add each separated input element to the dataToSendBack object as a key with its data let dataToSendBack = {}; if (Array.isArray(prepData)) { prepData.forEach((element) => { let [type, name] = element.split(":"); if (type === "r") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]:checked`) ?.value ?? null; } else if (type === "c") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]:checked`) ?.value ?? null; } else if (type === "s") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]`)?.value ?? null; } else if (type === "b") { dataToSendBack[name] = document.querySelector(`button[name="${name}"]`)?.value ?? null; } }); } else { let [type, name] = prepData.split(":"); if (type === "r") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]:checked`)?.value ?? null; } else if (type === "c") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]:checked`)?.value ?? null; } else if (type === "s") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]`)?.value ?? null; } else if (type === "b") { dataToSendBack[name] = document.querySelector(`button[name="${name}"]`)?.value ?? null; } } console.log(dataToSendBack); // Now we call the updateGame function with the latest fetchURI and the dataToSendBack object updateGame(fetchURI, dataToSendBack); } } }); } // This function inserts HTML from data into the gameDiv using innerHTML // Always and only pass the HTML data as a string to this function! function insertGameHTML(HTMLData) { gameDiv.innerHTML = HTMLData; } // Always fetch the current game data from the API when the page // loads to find out the current state for the current player! updateGame(fetchURI, null); // Function that makes POST fetch request to the API to update the game state // using the latest redirect URI and the data object passed to it! function updateGame(fetchURI, data) { fetch(fetchURI, { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json", }, body: JSON.stringify(data), }) .then((response) => { return response.json(); }) .then((data) => { console.log(data); // Check if response includes "error" key if (data?.error) { insertGameHTML(data.error); return; } // Otherwise insert the OK HTML data into the game! insertGameHTML(data.HTMLData); }) .catch((error) => { insertGameHTML( "<p class='text-center text-red-500 text-2xl font-OpenSansBold'>Misslyckades att uppdatera ditt nuvarande spel. Prova att ladda om sidan!"</p>" ); }); } });

Det jag gillar med lösningen är att det enda JS-koden gör är POST-anrop mot PHP-API:t och sedan för in via innerHTML-funktionen i ett särskilt div-element (likt ReactJS populerar ett div-element med RenderDOM) efterhållna HTML-data. På så vis har jag skapat en mycket förenklad och nischad variant av HTMX antar jag?🤔

Fast det som verkligen är "sexigt" är händelselyssnaren för klick som hämtar data om vilka inmatningsdata det ska skicka med (nedan är exempel på erhållen HTML i sträng från PHP-API:t):

<button data-dataToSendBack='r:menuChoice,b:submit' type='button' name='submit' class='h-[75px] w-[225px] mb-2 p-10 border-2 bg-[#ffffff] text-white py-2 font-OpenSansRegular font-bold rounded-lg hover:bg-[#ffffff] transition duration-200'>Bekräfta menyval</button>

Inuti finns alltså data-attributet "dataToSendBack" vilket i sin tur är en sträng som innehåller "pseudonyckelpar" vilket informerar frontend-delens JS-kod om vilka inmatningselements värden som ska skickas med när knappen klickas på. Den behöver inte bryr sig om valideringen för det sköts ändå hos PHP-API:t vilket bara skickar en ny HTML som då innehåller felmeddelandet om det skulle vara fel. (Och ja, den kommer att även skicka med tidigare inskrivna data!)

En intressant frågeställning är då:"Använda innerHTML är ju olagligt för tänk om <script> skickas med?!" Jag provade faktiskt detta och åtminstone i FireFox-webbläsaren så verkar JS för innerHTML-funktionen fungerar som om den ignorerar JavaScript-taggar? För jag såg ingenting om att CSP-huvudet reagerade på inline <script>-taggen vilket den annars gör om jag har med en sådan. (Ja, detta CSP-huvud är också konfigurerat att neka inline JS som t.ex. onClick="All Your Cookie Are Belong To Us").

Inuti PHP-API:t övergav jag också användningen av obfuskerade URI:s för jag lyckades ta fram en databastabell som kan hålla koll på slutanvändarens status i frontend-delen via vad som lagras i sessionskakan inklusive i en viss databastabellrad.

I nuvarande frontend-delsprototyp som jag visade upp igår så kan slutanvändaren logga in med eget återställt/skapat lösenord, välja ett "spel" som de är tilldelade (om de inte är tilldelade eller det har ej startat ännu så meddelas de detta och "spelet" startas ej), och välja och bekräfta ett menyval och har de valt något så får de samma meny men med grönt meddelande (som "proof of concept") och har de glömt att välja menyval så visas samma meny men med felmeddelande.

Den roliga utmaningen nu är att under vissa omständigheter så ska slutanvändaren fatta ett menyval och sedan kommer de att först fatta ett ytterligare menyval och sedan ska en nedräkningsklocka startas och skulle de ladda om sidan skarpt nu på mobilen så kommer nedräkningsklockan komma tillbaka och fortsätta där den slutade.

Därför kommer jag att lagra information om hur lång tid innan nästa POST-anrop får göras för just denna slutanvändare i "spelet". Sedan gör slutanvändaren ett nytt menyval efter nedräkningsklockan räknat ned och då anropat PHP-API:t typ, "Hörrö, jag har räknat ned nu, stämmer det på din sida också?".

Här kommer jag att göra som så att nedräkningen är exempelvis ett par minuter medan PHP-API:t lagrar att tiden den ska jämföra mot (klassiska time() i PHP) är ett par minuter minus en sekund för att inte råka orsaka situationen att anropet sker för fort och PHP-API:t nekar och JS-frontend-delen får förvirring vilket i sin tur väcker ofrivilligt missnöje hos slutanvändaren.

"Så hur bestämmer PHP-API:t vilken HTML ska skickas tillbaka då?"
Kortfattat så kollar först PHP-API:t att det är JSON-data som skickas och begärs tillbaka annars nekas det totalt. Sedan kontrolleras inloggning och att den unika slutanvändaren ifråga faktiskt har ett pågående "spel" annars nekas dem. När PHP-API:t tagit reda på att slutanvändaren har ett pågående "spel" så kollar den i en databastabellrad och har all information där som den behöver för att veta vilken HTML den ska skicka tillbaka härnäst.

All HTML-kod som skickas tillbaka innehåller som behövs: grafik, ett slags "inmatningsformulär" och en knapp som i sin tur innehåller information om vilka inmatningsdata som ska skickas tillbaka. Lita aldrig på klientbaserad validering så att inkludera information om hur inmatningsdata ska valideras innan de skickas är hugget som stucket beträffande valet. All validering sker hos PHP-API:t.

Vad som går att optimera/refaktorisera här är att först anta med en boolean att allt är OK (sant) och sedan kontrollera (en kontroll som ger fel sätter då boolean till falskt) och skapa eventuella felmeddelanden och sedan skicka tillbaka antingen samma HTML med felmeddelande (då har eller så skickas "nästa steg i spelet" (detta är när allt validerats rätt). Då är det bara att skicka tillbaka HTML med felmeddelande(n) (boolean falskt) eller "nästa steg i spelet"-HTML.

En annan optimering är att ta bort delen om att kolla efter data?.error och bara skicka HTML då felmeddelandet ändå bara är HTML också. Frontend-delen ska inte bry sig om att försöka kolla efter saker utan bara veta att föra in HTML eller egen HTML om den misslyckades få någon JSON-baserad responsdata.

Så de två mini-optimeringar och resterande är vad som gäller för nästa veckas arbete med avstämningsmöte nästa fredag som vanligt vilket är vad jag kallar för "agile-ish"-projektledningsmetod.

Ja, just det! Höll nästan på att glömma nu: Det sker särskilda kontroller på backend-sidan vilket endast kan inträffa om slutanvändaren skulle få för sig att försöka skicka "fuldata" / "fuzzing" med flit och därmed med flit inte använda den betalda tjänsten enligt överenskommelse. Detta kommer då Uppdragsgivaren att få reda på för att i sin tur kunna bestämma sig för vad de ska göra härnäst utifrån hur slutanvändaren har visat sig att använda den betalda tjänsten.

Exempelvis finns det hårdkodade inbakade data att skicka tillbaka (t.ex. välja en radioknapp eller kryssa i eller inte en ruta) och om då data då utanför omfånget skickas med så vet vi med 100 % att det har manipulerats någonstans längs vägen. Vi får anta slutanvändaren till att börja med då allt sker via HTTPS så det kan inte ha kapats på vägen. Visst, någon annan stal/använde mobilen och jävlades med slutanvändaren?!

Oavsett så är det alltid "bra kodövning" att försöka hantera alla möjliga specialfall även om de kanske typ aldrig kommer att inträffa. Fast hellre att kunna hantera dem när de väl nästintill omöjligen inträffar än att missa dem och skapa en dålig användarupplevelse i slutändan!

(Lugn! JS-koden kommer att minifieras/obfuskeras innan skarp sjösättning! )

Pssst! Jag har gömt ett slags "kod-påskägg" i koden någonstans om du kan hitta det och det är lite 🤪 när du inser följderna med det!

Hobbyprojekt
Inget nytt att rapportera.

Vidareutbildning
Inget nytt att rapportera.

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk
Skrivet av vg132:

Kan inte mycket om php men gissar att du har någon typ av sessionshantering även i php och det känns som att mycket av dina frågor/problem kan hanteras/lösas med hjälp av det. När du gör dina ajax anrop till servern så sparar du ner information i användarens session så att om användaren laddar om sidan så vet du var den befinner sig genom att läsa session informationen. T.ex. om du har flera steg som användaren ska gå igenom och varje stegbyte är ett ajax anrop, spara ner i session vilket steg användaren är på och om de laddar om så kan du bara återställa sidan så att den blir så som den ska vara vid frågan den fråga som är sparad i session.

Samma sak för ditt API, du kan ju i session kolla om användaren är inloggad och om den är i ett "state" där den får göra ett API anrop eller inte. "Security through obscurity" är inte alltid den bästa eller lättaste vägen

Bra att tänka på säkerhet när det gäller återställning av lösenord men tänk också på att just IP lås kan vara lite problematiskt när många jobbar med olika datorer, VPN, telefoner etc som kan ha olika IP'n. Du får kanske mailet till en annan enhet än den som du gör beställningen på. Men är det bara för admin användare kan det något de får leva med

/Viktor

Tjo igen! Tack så mycket för svaret. Jag såg ditt svar efter jag hade haft mitt "kodmaraton" igår så för det mesta hade jag då i största grad redan implementerat dina förslag där jag håller koll på slutanvändarens "game state" på backend-sidan genom $_SESSION-arrayen såväl som vad som finns lagrat i databasen!

Beträffande mejl så är tanken att slutanvändaren ska använda exakt samma enhet för att först fixa lösenordet och sedan börja med "den digitala modulen". Däremot att logga in och använda "den digitala modulen" fungerar för slutanvändaren även om IP-adressen ändras. Det administrativa är däremot strikt IP-begränsat av säkerhetsskäl (t.ex. session hijacking).

Mvh,
WKF.

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Förra fredagen hade jag avstämningsmöte med Uppdragsgivaren där bokstavligen talat beskrev att de är helt lyriska av den digitala modulen med sina olika faser och aktiviteter vilket slutanvändare kommer att kunna få ta del av efter att de först har administrerats av Uppdragsgivaren. Och detta var innan jag under denna vecka har gjort färdigt CSS-biten av webbplatsen utifrån erhållna prototypfiler.

Detta har gjort att frontend-JavaScript-koden har fått rejäla tillskott:

document.addEventListener("DOMContentLoaded", function () { gameDiv = document.getElementById("game"); const fetchURI = location.hostname === "localhost" || location.hostname === "127.0.0.1" ? `LOCAL_URL` : `ONLINE_URL`; if (gameDiv != null) { gameDiv.addEventListener("click", function (e) { const btn = e.target.closest("button"); if (btn) { if (btn.hasAttribute("data-dataToSendBack")) { const prepareDataToSendBack = btn.getAttribute("data-dataToSendBack"); let prepData = prepareDataToSendBack.includes(",") ? prepareDataToSendBack.split(",") : prepareDataToSendBack; let dataToSendBack = {}; if (Array.isArray(prepData)) { prepData.forEach((element) => { let [type, name] = element.split(":"); if (type === "r") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]:checked`) ?.value ?? null; } else if (type === "c") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]:checked`) ?.value ?? null; } else if (type === "s") { dataToSendBack[name] = document.querySelector(`select[name="${name}"]`).value ?? null; } else if (type === "t") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]`)?.value ?? null; } else if (type === "b") { dataToSendBack[name] = document.querySelector(`button[name="${name}"]`)?.value ?? null; } else if (type == "ta") { dataToSendBack[name] = document.querySelector(`textarea[name="${name}"]`)?.value ?? null; } }); } else { let [type, name] = prepData.split(":"); if (type === "r") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]:checked`)?.value ?? null; } else if (type === "c") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]:checked`)?.value ?? null; } else if (type === "s") { dataToSendBack[name] = document.querySelector(`select[name="${name}"]`).value ?? null; } else if (type === "t") { dataToSendBack[name] = document.querySelector(`input[name="${name}"]`)?.value ?? null; } else if (type === "b") { dataToSendBack[name] = document.querySelector(`button[name="${name}"]`)?.value ?? null; } else if (type === "bT") { dataToSendBack[name] = btn.value ?? null; } else if (type == "ta") { dataToSendBack[name] = document.querySelector(`textarea[name="${name}"]`)?.value ?? null; } } updateGame(fetchURI, dataToSendBack); } else return; } }); } function insertGameHTML( HTMLData, TimeRemaining = null, AnimateAddedPoints = null ) { gameDiv.innerHTML = HTMLData; if (TimeRemaining != null) { countdownTimer("timer", TimeRemaining); } if (AnimateAddedPoints != null) { animateAddedPoints("pointsDisplay", AnimateAddedPoints); } } updateGame(fetchURI, null); function updateGame(fetchURI, data) { fetch(fetchURI, { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json", }, body: JSON.stringify(data), }) .then((response) => { return response.json(); }) .then((data) => { if (data?.error) { insertGameHTML(data.error); return; } insertGameHTML( data.HTMLData, data.TimeRemaining ?? null, data.AnimateAddedPoints ?? null ); }) .catch((error) => { insertGameHTML( "<p class='text-center text-red-500 text-2xl font-OpenSansBold'>Misslyckades att uppdatera rattmuffarna. Prova att ladda om sidan!</p>" ); }); } function countdownTimer(id, timeRemaining) { let timer = document.getElementById(id); let minutes, seconds; let x = setInterval(function () { minutes = Math.floor(timeRemaining / 60); seconds = timeRemaining % 60; seconds = seconds < 10 ? "0" + seconds : seconds; timer.innerHTML = minutes + ":" + seconds; if (timeRemaining <= 0) { clearInterval(x); updateGame(fetchURI, null); } timeRemaining--; }, 1000); } function animateAddedPoints(id, points) { window.scrollTo(0, 0); let pointsDisplay = document.getElementById(id); let animatePoints = document.getElementById("animatePoints"); let currentPoints = parseInt(pointsDisplay.textContent); pointsDisplay.textContent = `${currentPoints - parseInt(points)}`; if (points < 0) { animatePoints.classList.remove("text-green-500"); animatePoints.classList.add("text-red-500"); } else { animatePoints.classList.remove("text-red-500"); animatePoints.classList.add("text-green-500"); } if (points > 0) { animatePoints.innerHTML = `+${points} poäng`; } else { animatePoints.innerHTML = `${points} poäng`; } animatePoints.style.display = "block"; animatePoints.classList.add("animate__animated", "animate__fadeInUp"); setTimeout(() => { animatePoints.classList.remove("animate__animated", "animate__fadeInUp"); animatePoints.style.display = "none"; pointsDisplay.innerHTML = `${currentPoints}`; }, 1000); } });

Nu ansvarar frontend-JavaScript-koden över att inte bara uppdatera det pågående spelet utan också animera eventuella erhållna poäng, samt agera som nedräkning under vissa omständigheter. Allt detta beror på vad updateGame(fetchURI, data)-funktionen får tillbaka i JSON-datat.

På backend-sidan så blev det då att ändra de HTMLData-strängar till det nya utseendet utifrån erhållna prototypfiler. Detta var både utmanande och givande. För en gångs skull kan jag säga att jag är "tillräckligt nöjd" med den frontend jag har knackat fram med hjälp av Tailwind CSS och majoriteten av det är primärt tack vare utomordentliga prototypfiler att utgå ifrån.

Andra tacksamheten är från gratisversionen av chatGPT4o vilket jag till och från fick snabba och smidiga CSS-lösningar, vissa bättre än andra. Men de gav utgångspunkter och "good enough"-lösningar till och från. Exempelvis följande där jag roterar en div och sedan roterar tillbaka en bild så att den ligger rakt trots att själva div är något lutad med nedre vänstra delen som "ankare":

<div class='relative mt-[13vw] w-full transform rotate-[-5deg] origin-bottom-left h-[70px] bg-rattmuff flex flex-row justify-between items-center align-middle'> <p class='italic pl-[3rem] font-OpenSansBold text-3xl text-white'>Rattmuffen anfaller</p> <img class='transform rotate-[5deg] h-[75px] w-[75px] mr-[2rem] absolute right-1 bottom-2' src='" . images("rattmuff-icon.png") . "'> </div>

(Nej, detta nuvarande pågående uppdrag har ingenting med rattmuffar att göra oavsett hur många gånger jag obfuskerar med rattmuffsdata!🤣)

Däremot var det jag som valde att köra med mt-[13vw] för att lösa ett problem som ändå inte är perfekt. Denna div ser nämligen fasansfull ut på större skärmar men på mobiltelefoner jag testat så ser de "relativt" (hah! ) ut som tänkt och enligt erhållna prototypfiler.

Tack vare att jag har olika funktioner för det mesta - projektet är till 95 % funktionsbaserat - så går det lätt att göra ändringar i utseendet medan övrig "funktionalitet" (hah! ) kvarstår. Så nu går det att slutföra ett spel i en given fas. Det som ska diskuteras imorgon - såväl som det "skarpa fysiska testet på plats" - blir några ytterligare funktioner som önskas enligt överenskomna kravspecifikationen.

Det ska bli spännande och roligt att se om Uppdragsgivaren blir ännu mer lyrisk imorgon bitti när de får ta del av deras digitala modul som nu också rätt så ser ut som de önskar det att se ut.🫡

Ja, just det! Det var en klurig grej att fixa vilket är när att ett spel ska laddas in så kontrolleras först att slutanvändaren är inloggad, har något spel att spela och rätt fas att spela. Kruxet var dock att beroende på vilken fas de ska spela så måste en viss bakgrundsbild visas men som också ska vara genomskinlig:

.rattmuff-bg-url1 { background-image: url("http://localhost:8080/rattmuffarnas_hemlighet/images/rattmuff-fas1-bg.jpg"); background-size: cover; background-position: center; background-repeat: no-repeat; background-blend-mode: overlay; background-color: rgba(255, 255, 255, 0.7); }

Därför behövdes följande klass att renderas inuti samma div som sedan ändrar sin innerHTML utifrån erhållen JSON-data. Problemet var då att jag hade vissa funktioner i en ordning som gjorde att vilken klass - .rattmuff-bg-url1, .rattmuff-bg-url2 eller .rattmuff-bg-url3 - som skulle visas inte var känt innan den lades in i div som renderar om via innerHTML vilket ledde till att ingen bakgrund utifrån rätt fas kunde visas.

Detta inträffade främst om det var första gången slutanvändaren startade spelet för då fanns det inte inlagt i databasen i en viss tabell över vilken fas de spelade just då. Tur var för mig i och med det "modulära" tänket med funktionerna så var det bara att flytta om vissa funktioner så jag kunde först starta spelet efter kontroll om slutanvändaren har något spel och en fas att spela, därefter kunde jag hämta rätt fasnummer och därmed visa rätt bakgrundsbild!🥳

Vad som återstår sedan efter det fysiska avstämningsmötet imorgon är som sagt var ett par funktioner till för att kunna slutföra varje fas korrekt för varje enskilda slutanvändare. Sedan så har vi också önskade statistikfunktioner där det ska gå att plocka ut anonyma data om hur slutanvändare har spelat under en given fas och detta önskas som tidigare nämnt i Excel eller något annat sätt där det kan visualiseras.

Således funderar jag på först lagra data i databasen som sedan kan skrivas ut i Excel-fil och går det också via PhpSpreadsheet-biblioteket att generera visualiserande formler i samma Excel-fil så vore det guld värt. Annars får jag hitta något annat bibliotek som kan visualisera inmatade data på något vis och det måste - på gott och ont - vara PHP-baserat då sjösättningsplatsen för webbplatsen inte stödjer exempelvis NodeJS där det annars finns tonvis med JavaScript-bibliotek för att visualisera data.

Eller snarare är det väl att jag vill inte att så mycket ska vara synligt på frontend utan det är bättre(?) tänker jag att frontend-JavaScript-koden anropar om något och bara infogar säker innerHTML på en given plats så behöver den heller inte bry sig om implementationsdetaljerna utan det sistnämnda får backend lösa och ansvara över! Och då PHP är "serverkod" - som "kodbönderna" säger - så blir det det perfekta valet allt som allt.

Jag inflikar imorgon eftermiddag med en snabb uppdatering om Uppdragsgivarens - förhoppningsvis - återigen lyriska användarupplevelse av deras digitala modul som då har "testats i skarpt läge" fysiskt på plats!

Hobbyprojekt
Inget nytt att rapportera.

Vidareutbildning
Inget nytt att rapportera.

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jobb & Uppdrag
Idag runt 08:30-tiden var jag på plats i staden och med utkik ned mot det lokala universitetet på högsta våningen i en byggnad med mörkgröna gardiner så "spelade" jag och Uppdragsgivaren såväl som en ytterligare samarbetspartner till dem. De var/är mycket nöjda med vad jag har åstadkommit i det visuella utifrån erhållna prototypfiler i frontend-delen. Jag fick även höra lite mer om deras framtidsplaner och här passade jag då på att inflika ytterligare framtida uppdrag eftersom någon gång i början på september i år så är uppdraget slutfört/fullständigt levererat.

De berättade då att de troligen kommer att ha fler framtida uppdrag åt mig. Det hela ska tydligen testas i skarpt läge hos någon eventuell framtida slutkund med slutanvändare någon gång efter juletider i år. Jag har buffert så jag klarar mig samtidigt som jag kommer så klart att söka vidare efter fler uppdrag ...

Mini rant
... men jag är relativt säker på att jag inte kommer att lyckas hitta några för jag konkurrerar ju trots allt mot nyligen(?) arbetslösa seniorer här och var. För vad jag vill komma till är återigen min ena nuvarande form av "Imposter Syndrome"eller kanske snarare "Not-Impressed Syndrome". Om vi tar en titt på den teknikstack jag har använt mig av utifrån de begränsningarna som finns med webbhotellsleverantören Loopia och att Uppdragsgivaren ej vill prova på VPS så är det följande:

  1. - vanilla JavaScript, HTML och Tailwind CSS för frontend

  2. - vanilla PHP inklusive ett par PPH-bibliotek, samt MySQL för backend

Det är liksom ingen ReactJS, ingen NodeJS/Bun eller Laravel eller Ruby i backend, ingen PostgreeSQL eller MongoDB som lagringslösning, ingen Docker, ingen Azure, AWS, eller liknande "moderna seniora webbarkitekturer" som troligen finns i fina portföljer med slutförda projekt och starka referenser om hur det utvecklades från start till "slut". Självfallet menar jag inte på att andra inte skulle ha stött på hinder till och från precis som jag har gjort och haft mina skriftliga uttryck för!😂

Med "Not-Impressed Syndrome" menar jag på att "Hur imponerande är detta egentligen för de som är IT-insatta?". Det är lite som att göra CGI-effekter för de som ej arbetar med det och därmed kan bedöma om det är "På en bra nivå med god framtidspotential" eller om det fortfarande är "Rätt så lattjolabjan av det hela". När det gäller mina designförmågor så är de fortfarande på rätt så "lattjolabjan"-nivå.

Vad jag då "oroar" mig över är att det jag producerat inte är så riktigt "imponerande" rent tekniskt talat även om min Uppdragsgivare är mer än nöjd. Jag har ju inte använt "häftiga branchstandardbibliotek" utan jag har använt av vad som fortfarande fungerar. Det finns inga otroliga komplexa algoritmer med nödvändiga civilingenjörsmässiga matematiska förkunskaper för att kunna förstå. Det är simpel kod för det är funktioner - många av dem - vilket samverkar för att åstadkomma vad som i sin helhet kanske skulle kunna klassas vara "Kinda kewl, d00dl!"?🤔

I en Discordgrupp jag är med i så diskuterar jag just nu om det här med "talang", "fallenhet" och liknande begrepp för att argumentera för att "En ska investera i sina styrkor" och med "styrkor" menar jag då saker som en har "lätt för att bli bra och duktigare på" jämfört med något annat som de kanske kan bli bra och duktigare på men det skulle ta en relativt o rimligt lång tid för att det skulle vara värt det (individuell uppfattning så klart). Någonstans minns jag att jag hörde uttrycket "Fortsätt att stärka dina styrkor istället för att försöka stärka dina svagheter".

Jobb & Uppdrag (forts.)
Okej, det nästa efter "val" av teknikstack är vilka designmönster och allmän arkitekturmässiga lösning som har använts. Här har det hela mer eller mindre varit funktionsbaserat, det vill säga, majoriteten av allt är funktioner som då körs i en viss given ordning. Det finns inga fler klasser än de som krävs för olika PHP-bibliotek eller databasanslutningsobjektet. Jag älskar funktioner för de måste inte alltid vara "rena". Du kan ju ha parametrar som är $options där du då vet att om du anropar funktioner med något i $options så kommer det att bete sig annorlunda samtidigt som det för övrigt gör sin "rena" funktionalitet.

Tack vare funktioner så kan du också följa dem och deras "spaghettighet" är i samma smak som kodkocken Mannerström vilket är individuellt hur det smakar i slutändan:

Personligen älskar jag funktioner även om det också finns mys med att instansiera objekt från klasser och veta att det instansierade objektet nu har olika "medfödda" egenskaper och metoder som går att nyttja specifik för det objektet istället för att behöva lagra eller bygga in i en variabel som bygger upp sitt eget "tillstånd" via olika variabelnycklar med värden erhållna från diverse funktioner här och var.

... Hoppar jag framåt lite nu i tankarna så finns det en sak jag har anmärkt på min användning av funktioner och det är just det här med komponenttänkandet som finns i många "ramverk" och kodbibliotek där du skapar komponenter som sedan kan innehålla subkomponenter, och så vidare. Exempelvis har jag följande funktion:

function playerInfoString($player, $points, $phase, $nextRattMuff){}

Och den returnerar helt enkelt en HTML-sträng där spelare, poäng, fas och nästa "rattmuff" hamnar och denna HTML-sträng är bara en del av flera som sätts ihop inuti en annan funktion som i sin tur returnerar en associativ array med HTMLData vilket används av frontend för att rendera det interaktiva för slutanvändare. Sedan kom jag på att jag kan ju ha flera nu:

function playerMainMenuString($secret_number){} function timerString($phase){} function timerCompleteString($phase){} function timerRotateString($phase){} function returnToMenuString(){} function returnToPlayMenuString(){} function noToMenuString(){}

Dessa kombineras sedan i en annan funktion som ska returnera en del:

return [ HTMLData =>"... </div>" . playerMainMenuString($secret_number) . "</form> </div>" . returnToMenuString() ];

Och vips så har jag i princip "byggt" en strängkomponent med flera små strängbaserade subkomponenter!

Detta gav en liten "aha!"-upplevelse av varför många "ramverk" och kodbibliotek gör som de gör med sina komponentbaserade lösningar. På så vis så försöker jag delvis återuppfinna många fina redan uppfunna hjul för att få någon slags insikt i vad som händer under den "kodbaserade motorhuven". Just förmågan att kunna återanvända dem ger mycket när antalet komponenter växer och ett konsekvent utseende vill bibehållas.🫡

Hursomhelst så blir nästa steg nu nästa vecka att fixa det som blev överenskommet på det senaste - och denna gång det fysiska -avstämningsmötet ihop med Uppdragsgivaren. Och efter det så blir steget därpå att ordna så att statistik går att inhämta och sammanställa. Här kommer jag nog att behöva ta del av diverse PHP-baserade bibliotek som sagt var tidigare.

Hobbyprojekt
Inget nytt att rapportera.

Vidareutbildning
Inget nytt att rapportera.

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jag börjar uppdateringen med hajpade nyheter om hur det känns att vara färdigutbildad (men inte färdiglärd så klart) webbutvecklare enligt bild ovan vilket också visar min hajp för den kommande Mange på Momangen-filmen nummer tre i december i år.

Jobb & Uppdrag
Om drygt en vecka är mitt första uppdrag sedan min högskoleexamen inom datateknik med inriktning inom fullstacks-utvecklare i webbutveckling färdigt - och det hela har rullat på helt galant med till och från olika utmaningar och möjliga "skrik" framför arbetsdatorn!😂

Jag har redan varit i samtal om framtida fortsatta uppdrag från samma Uppdragsgivare samtidigt som det ska bli skönt att få lite "ledighet" efter det drygt åtta veckors långa uppdraget. Enligt den påskrivna offerten så ska den tredje delbetalningen faktureras någon gång under vecka 36 vilket är nästa vecka.

Samtidigt sa Uppdragsgivaren att jag får skicka den tredje delbetalningen efter slutgiltig leverans om vi kallar det för det. Jag är medgörlig och flexibel och de tidigare delbetalningarna har alltid kommit in i tid så jag har inga problem med detta. Detta är trots allt det första uppdraget och det kodade arbetet där jag fått ersättning för.

I samma veva så är det självfallet viktigt att mycket mer konkret och begränsat ta fram Kravspecifikationer som inte innehåller "kryphål för extra icke-ersatt arbete". Nu anser jag att denna nuvarande pågående Uppdragsgivare är mycket medgörlig, förstående och respektfull för mina förmågor så samarbetet har och fortsätter att flyta på!🫡

Så det hela samarbetet har varit och är tack vare vår gemensamma öppna kommunikation och ömsesidiga respekt för varandras olika domäner(?). På tal om andra framtida Uppdrag så hörde en mycket god vän av sig till mig och frågade om jag "Fixar hemsidor åt folk?!" vilket jag så klart gör!

Den gode vännen är med i vad som hamnar under ideell förening, en klubb inom en viss sport, och deras klubb behöver en "simpel hemsida" för saker som priser, träningsschema, om oss, och en startsida. Den gode vännen frågade om jag kunde fixa något då mot eventuell betalning. Min utmaning är dock som många gånger tidigare sagt det estetiska för webbplatser och inte så mycket det funktionella.

Vad gäller det fortfarande pågående ersättningsbelagda arbetet: JS-koden i frontend-delen har fått tillskott för att kunna växla med att visa/dölja nödvändig aktivitetsinformation vid särskilda val inuti den webbaserade digitala modulen:

if ( document.getElementById("activityDescGet") && document.getElementById("activityDescShow") ) { const activityDescGet = document.getElementById("activityDescGet"); const activityDescShow = document.getElementById("activityDescShow"); activityDescGet.removeEventListener("change", function () { activityDescShow.textContent = activityDescGet.options[activityDescGet.selectedIndex]?.getAttribute( "data-activity-description" ) ?? ""; }); activityDescGet.addEventListener("change", function () { activityDescShow.textContent = activityDescGet.options[activityDescGet.selectedIndex]?.getAttribute( "data-activity-description" ) ?? ""; }); activityDescShow.textContent = activityDescGet.options[activityDescGet.selectedIndex]?.getAttribute( "data-activity-description" ) ?? ""; }

Notera hur koden börjar med att ta bort eventuell tidigare händelselyssnare innan den skapar en ny. Om det inte finns någon så kommer detta ej att resultera i någon krasch och det förbättrar minneshanteringen så det inte blir onödiga kopior av samma händelselyssnare även om jag tror att jag läst någonstans att exakt samma händelselyssnare på samma element inte leder till någon kopia?🤔

Nu när det allra första Uppdraget sedan Webbutvecklingsprogrammet snart är över så undrar Du säkert vad jag har åstadkommit dessa snart dryga åtta veckor? Först och främst så har det som skapades under examensarbetet utökats, vissa delar har förbättrats (exempelvis har jag implementerat flikar för vissa administrativa delar) såväl som att nya delar har tillkommit.

Även en helt ny webbplats har uppstått genom kodandet vars syfte är till att nyttjas av slutanvändare som ska kombinera en digital modul (frontend-webbplatsen med annat domännamn) och en fysisk aktivitet gemensamt med andra slutanvändare. Uppdragsgivaren räknar med att gärna hundratals kanske tusentals deltagare ska nyttja den digitala modulen samtidigt.

Jag ska höra med Uppdragsgivaren i vilken mer utsträckning jag kan berätta om projektet, möjligen när det har sjösatts skarpt för det kommer att bli marknadsföring och andra relevanta saker att göra såväl som vidareutveckling. Avtäckandet kommer att äga rum i samband med lokalevenemang där jag bor.

Vad jag dock oroar jag mig över gällande uppdragets slutgiltiga resultat är över Loopia (eller kanske "Poopia" då?) som webbhotelleverantör inte kanske kommer att kunna hålla måttet med tusentals anropar mot inte bara webbplatsens REST API utan också tillhörande databas? Jag har som sagt var noll kunskaper och insikter i hela biten med sjösättning, drift och optimering av webbplatser än så länge.

Jag behöver lära mig om "Load Balancing", "Rate Limiting", "Requests Queuing", "Optimized SQL Queries", "Cache Management", o.d., så att jag verkligen kan ta mig an rollen som fullvärdig fullstacks-utvecklare eller finns det så kallade "deployers"-utvecklare som bara sjösätter, driftar och möjligen rapporterar in problematik in till utvecklarna som har behörighet att korrigera "dåligt optimerad kod"?

På tal om "Rate Limiting" så undrar jag om det delvis är sanning med modifikation: En server måste ju veta först vem som gör ett anrop (IP-adress) och sedan jämföra mot något (whitelist, blacklist, databas, cache, etc.) för att veta om den ska släppa vidare eller inte. Vad jag föreställer mig då är två servrar: 1) En server som bara kontrollerar hela Rate Limiting medan 2) En andra server sköter faktiska godkända anrop. Jag tänker vidare att om samma server sköter detta så är det ju ändå anrop som måste hanteras delvis för att antingen nekas eller hanteras vidare så det blir ju ändå anrop oavsett vad då. Är jag rätt spår här i mitt tänkande?🤔

Det jag har gjort senast är att ha tagit fram databastabeller som kan lagra statistik om slutanvändarnas deltagande på olika sätt och vis. Vad som återstår nu är att visuellt presentera det med hjälp av något JS- och/eller PHP-bibliotek. chart.js har jag föreslagits i Discord och JpGraph har jag kikat på och så ska jag läsa mer här om vad som finns: https://www.bairesdev.com/blog/php-charts/.

Har du något förslag om JS- och/eller PHP-bibliotek för att visuellt (exempelvis simpla paj-/munk- och/eller stapeldiagram) representera numerära data? Hojta gärna till här då!

Hobbyprojekt
Jag har inte gjort något ytterligare rent konkret talat men i takt med det pågående uppdraget så har jag funderat på saker som var någonting ska köras. Exempelvis funderar jag på arrayer som ska agera som "middlewares". Exempelvis en array som innehåller vilka REQUEST_URIs som måste auktoriseras på det ena eller andra sättet vilket då gör att varje ändpunkt som startar med en viss REQUEST_URI inte måste konfigureras med samma auktoriseringskrav.

En annan sak jag tänkt på är valideringsfunktioner av erhållna data. För när jag tänker på utvecklingen av det funktionella ramverket som troligen bara jag kommer att använda till en början så funderar jag inte bara på hur det ska fungera utan också upplevelsen av att använda det.

Detta för mig till två saker: 1) ett mycket förenklat GUI och 2) hårdkodade variabler som ska användas mycket av utvecklarna.

Vad menar jag då? Jo, jag har ju funderat på:

$d['p']['key'] = "värde från POST-variabel"; $d['g']['key'] = "värde från GET-variabel"; $d['j']['key'] = "värde från JSON (php://input)"; $d['f']['key'] = "värde från FILES-variabel";

Men notera hur irriterande detta delvis är: Du måste först skriva $d, sedan [] och 'p' inuti. Varför inte bara skriva:

$dp['key'] = "värde från POST-variabel"; $dg['key'] = "värde från GET-variabel"; $dj['key'] = "värde från JSON (php:\/\/input)"; $df['key'] = "värde från FILES-variabel";

Så du kan bara skriva $dataPOST['key'] i princip istället för massa annat. I början kan det verka irriterande men den som tycker om att skriva skript där du kör dem med kortkommandon och någon exekveringstangent så blir det guld värt i det långa loppet efter kort inlärningskurva. Nackdelen är när det blir för många av dem, då börjar du komma av dig efter du inte använt dem på ett tag.

Så vad var det då med valideringsfunktionerna? Jo, många ramverk har så att du måste ange "required" för att ange att en erhållen variabel är obligatorisk, men varför håller du då på att validera den för tänker jag? Det borde snarare vara att det är per standard "required" så vad du sätter är snarare "optional" om du inte vill kräva. Och då slipper du skriva "required" för alla variabler att validera ytterligare.

För hur ska du kunna validera vidare en variabel om du inte ens krävt den först? Och hur många variabler ska du egentligen låta vara valfria till att börja med? Då börjar jag fundera på vad det är för slags data som ska inhämtas. Visst finns det sådana situationer där du har flera valfria fält. Men den dagen, den sorgen med att nöta in "optional" här och var? Majoriteten av fält är väl "required" för det mesta? Eller?

Vidare kan vi utveckla det hela där vi inkluderar felmeddelanden (från valideringsfunktioner) och sanerade värden i enskilda nycklar:

$dp['key']['err']['number'] = "Felmeddelande om POST-nyckeln 'key' gällande att datatypen måste vara numeriskt värde"; $dp['key'][val'] = "Värdet från POST-nyckeln 'key'";

Men detta är ju smått irriterande med de överflödiga hakparenteserna återigen. Varför inte tänka:

$dpe['key']['number'] = "Felmeddelande om POST-nyckeln 'key' gällande att datatypen måste vara numeriskt värde"; $dpv['key'] = "Värdet från POST-nyckeln 'key'";

Det är nu vi börjar närma oss det jag varnade för: Många korta variabelnamn vilket kan jämföras med kortkommandon inom skriptande med en exekveringstangent (exempelvis AutoHotKey som jag har använt mig av i snart tre år). Dessutom behöver vi använda en funktion som sköter det simpla med att inte försöka skriva ut icke-existerande variabler och/eller standardvärden vid null i respektive variabel.

Lösningen är ju busenkel dock men går också emot det jag tänker mig med detta PHP-ramverk; konvertera om dem till objekt och bara kom åt allt via exempelvis:

$pErrNumber = $dp->err->number; // Felmeddelande när nyckeln ej är numerisk $pValue = $dp->key->value; // Värdet från POST-nyckeln 'key'

Vad gillar/föredrar du för slags datastrukturhantering inom PHP?
Jag har full förståelse att Laravel är det som gäller och det är det!😁

Vidareutbildning
Inget nytt att rapportera.

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk
Medlem
Skrivet av WebbkodsFrilansaren:

Notera hur koden börjar med att ta bort eventuell tidigare händelselyssnare innan den skapar en ny. Om det inte finns någon så kommer detta ej att resultera i någon krasch och det förbättrar minneshanteringen så det inte blir onödiga kopior av samma händelselyssnare även om jag tror att jag läst någonstans att exakt samma händelselyssnare på samma element inte leder till någon kopia?🤔

Det är beundransvärt att du tänker på eventuella minnesläckor i din kod. Jag känner mig dock tvungen att påpeka att koden ovan inte kommer fungera som du tänkt. Det går inte att anropa removeEventListener med en anonym funktion. Även om den råkar göra exakt samma som den i addEventListener kan JavaScript inte jämföra dem och plocka bort lyssnaren. Du behöver bryta ut funktionen du kör där och ge den ett namn.

Permalänk
Skrivet av cfj:

Det är beundransvärt att du tänker på eventuella minnesläckor i din kod. Jag känner mig dock tvungen att påpeka att koden ovan inte kommer fungera som du tänkt. Det går inte att anropa removeEventListener med en anonym funktion. Även om den råkar göra exakt samma som den i addEventListener kan JavaScript inte jämföra dem och plocka bort lyssnaren. Du behöver bryta ut funktionen du kör där och ge den ett namn.

Tjo! Tack så mycket för den snabba återkopplingen på det jag har gjort!

Löser detta det då? Nu anger jag vilken typ av händelse samt funktion som sköter det, och så tas den bort först innan den skapas:

function updateActionDesc() { const actionDescGet = document.getElementById("actionDescGet"); const actionDescShow = document.getElementById("actionDescShow"); actionDescGet.removeEventListener("change", updateActionDescHandler); actionDescGet.addEventListener("change", updateActionDescHandler); // Also set current one when first loading the page updateActionDescHandler(); function updateActionDescHandler() { actionDescShow.textContent = actionDescGet.options[actionDescGet.selectedIndex]?.getAttribute( "data-action-description" ) ?? ""; } } if ( document.getElementById("actionDescGet") && document.getElementById("actionDescShow") ) { updateActionDesc(); }

Mvh,
WKF.

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"

Permalänk

Webbutvecklingsdagbok - efter studierna

Jag har en kort och snabb uppdatering idag.

Jobb & Uppdrag
SMS och samtal erhölls idag runt 13-tiden där jag fick besked om små problem med det administrativa så jag korrigerade det. Det hela handlade om databasförfrågningar som använder sig av INNER vs LEFT JOIN.

Jag läser här om JOINS inom SQL: https://www.w3schools.com/sql/sql_join.asp och där framgår det klart och tydligt att kör jag INNER JOIN så måste det finnas matchande värden i båda tabellerna som försöks slås samman i databasförfrågningen.

Det finns nämligen fallet att en så kallad "aktivitet" kanske togs fram av en administratör utan något förslag från en deltagare ELLER så var det så att deltagaren kom med förslag vilket den skapade "aktiviteten" togs fram utifrån med grund. Då ska det finnas med information om denna deltagare, dvs., data från "deltagartabellens" rad ska finnas på samma rad som "aktivitetstabellens" gemensamma rad även om det är NULL, dvs., det finns ingen deltagare som föreslagit en del av innehållet till den "aktiviteten" på samma rad.

Vad som hände var att det fanns inget id för en given aktivitet så det gick ej att redigera en aktivitet eftersom aktivitetens id var null och tomt så href-länken som skrevs ut var en tom sträng då jag tillämpar <?= $variabel ?? null ?> för variabelutskrift. Hittills har alla mina sammanslagna tabelldatabasförfrågningar utgått från att data alltid finns i de inkluderade tabellerna så jag har bara kört INNER JOIN utan att tänka mer på saken.

Under föregående vecka när jag implementerade mer flexibla funktioner till diverse undersidor så dök detta intressanta problem upp vilket min Uppdragsgivare rapporterade mig om. Det är antingen ikväll eller imorgon som Uppdragsgivaren ska testköra den digitala modulen i skarpt läge med helt icke-insatta användare för att få användartestdata om hur de upplever själva användargränssnittet som icke-insatta deltagare även om de inte nödvändigtvis är de faktiska slutanvändarna sett utifrån en målgrupp/ett marknadssegment.

Nästa måndag får jag då en redogörelse för de övriga "puckar" att fixa innan den slutgiltiga leveransen av den digitala modulen. I tidigare kortfattat telefonsamtal idag med Uppdragsgivaren så berättade Uppdragsgivaren att de gärna kommer att anlita mig ett par timmar i månaden för att kunna fixa små saker i takt med att de övergår till försäljning av deras kombination av fysiskt och digitalt baserade produkt. Jag sa också i samma veva att om det dyker upp små saker som måste akut fixas så är det bara att skicka sms, mejla eller ringa mig så fixar jag det.

Min syn på det hela är följande: Att små saker upptäcks t.ex. buggar och/eller användarfel är också återkoppling jag får tillbaka på vad jag själv missat under utvecklingsprocessen såväl som uppdragstagare i detta allra första ersättningsbelagda uppdrag. Eftersom det diskuteras ytterligare ersättning för att fixa felen så är det inte som så att jag är någon gratis kundsupport efteråt. Denna Uppdragsgivare respekterar och därmed värdesätter min kompetens även om den skulle kunna ifrågasättas i och med eventuella upptäckta småfel så här i efterhand!

Jag ser alltså ett värde i sig att få veta att något måste fixas för då kan jag även se till att liknande inte upprepas i framtida uppdrag/arbetsuppgifter. Samtidigt är det alltid en avvägning mellan hur mycket som görs för erfarenhetens skull och vad en ska ta betalt för.

Om du har synpunkter kring mitt ovannämnda resonerande som "färsking" inom IT-konsultvärlden så får du gärna dela med dig av dessa här!

Hobbyprojekt
Inget nytt att rapportera mer än att lärdomarna från uppdraget i hur SQL ska appliceras så att det fungerar som tänkt får mig då att fundera vidare på någon form av funktionsbaserad ORM eller inte till det kommande 99 % funktionsbaserade PHP-ramverket.

Vidareutbildning
Inget nytt att rapportera.

På återseende!

Mvh,
WKF.
---------
✔️HT2022-VT2024 TWEUG Webbutvecklings-programmet 120hp (distans)

Visa signatur

"Den säkraste koden är den som aldrig skrivs"
"Visste du förresten att det är ett mångmiljardbolag?"
"Jag lever inte för att koda utan kodar för att sen kunna leva"