Webbutvecklingsdagbok (Webbutveckling, 120 hp)

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Nu börjar har jag mer eller mindre hamnat i fas för de två pågående kurserna (Databaser & Webbutveckling II).

Nedanför återfinns min inlämnade Labb 1 (eller "Moment 1") i Databaser-kursen [VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)]:
<Uppladdad bildlänk>

Det är en ER-modell för ett hypotetiskt filmuthyrningsföretag. Den har ej ännu rättats och den som upptäcker något "allvarligt uppenbart fel" får gärna anmärka på det!

Enligt en som läser år 2 i detta distansprogram sägs det att läraren verkar vara riktigt petig och att, "Det finns bara perfekta databaser som godkänns!". Det återstå att se hur det är med den saken. Många verkar än så länge ha fått kompletteringskrav (Fx) dock.

Resten av inlägget är kodkopior av Moment 2 i Webbutveckling II [VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)] då git-klonen är privat mellan mig och läraren (de använder GitHub Classroom för momenten).

Alla p-filer ("pages") ligger i rotkatalogen medan c-filer ("components") och config-filen ligger i underkatalogen include för den som vill lägga in allt och provköra med valfri Apache-server. CSS-filen i CSS-mapp såklart.

index.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=0; ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <?php include("p-questions.php");?> <?php include("include/c-footer.php");?>

Dold text

css/styles.css

/* =============================== NORMALIZING + FONTS - All Pages =============================== */ * { padding: 0; margin: 0; box-sizing: border-box; font-size: 1rem; font-family: 'Orbitron', sans-serif; } /* ================== <BODY> - All Pages ================== */ body{ width: 100%; margin: 0 auto; } /* ======================== FONT ELEMENTS - Standard ======================== */ h1{ font-size: 3rem; } h2{ font-size: 2.1rem; text-align: center; margin-bottom: 20px; } h1,h2,h3{ font-family: 'Orbitron'; font-weight: 800; } h3{ font-size: 1.4rem; text-align: center; margin-bottom: 10px; } span, i, p{ font-size: 1.3rem; line-height: 1.5; max-width: 50em; } /* ======================== .main-layout div ======================== */ .main-layout{ min-height: 100vh; min-height: 100dvh; display: grid; grid-template-rows: auto 1fr auto; background-color: rgb(56,59,58); } /* ======================== <nav> & .menu-btn ======================== */ .menu-btn{ display: inline-block; width: 200px; padding: 20px; font-size: 20px; font-weight: 700; cursor: pointer; border: none; background: #383b3a; color: #1bbb85; border: 2px solid #1bbb85; border-radius: 5px; text-align: center; margin: 10px; transition: background 400ms ease-out, color 400ms ease-out; } .current-btn{ box-shadow: 5px 5px 5px #1bbb85, 5px -5px 5px #1bbb85, -5px 5px 5px #1bbb85, -5px -5px 5px #1bbb85; } a{ color: #1bbb85; text-decoration: none; } .menu-btn:hover, .menu-btn:focus{ background: #1bbb85; border-radius: 5px; } .menu-btn:hover > a, .menu-btn:focus > a{ color: #383b3a; text-decoration: underline; } #nav-ul{ margin-top: 1rem; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-evenly; } /* =========================== <main> =========================== */ main{ margin: 0 auto; padding-left: 10%; padding-right: 10%; } /* =========================== HEADER & FOOTER SETTINGS =========================== */ header, footer{ background: rgb(56,59,58); background: linear-gradient(0deg, rgba(56,59,58,1) 0%, rgba(27,187,133,1) 35%, rgba(128,31,125,1) 100%); } header{ padding: 3%; text-align: center; } footer{ padding: 3%; text-align: center; font-weight: bold; } #footer-text{ font-size: 0.9rem; line-height: 1.5; } .main-layout{ min-width: 390px; } /* =========================== page: questions =========================== */ .questions{ margin-top: 20px; margin-bottom: 20px; list-style-type:decimal; font-size: 2rem; } .questions > strong{ display: inline-block; font-size: 1.4rem; max-width: 50em; } /* =========================== page: variables =========================== */ .uppgifts-div{ background-color: white; height: fit-content; border: 2px solid #1bbb85; border-radius: 5px; padding: 20px; box-shadow: 2px 2px 3px #1bbb85, 2px -2px 3px #1bbb85, -2px 2px 3px #1bbb85, -2px -2px 3px #1bbb85; margin-bottom: 30px; } .variabel-ul{ display: flex; flex-direction: column; justify-content: center; align-items: center; } .variabel-ul > li{ width: 250px; } .variabelP{ font-size: 1.1rem; text-align: center; } .variabelP > a:hover, .variabelP > a:focus, .backA:hover, .backA:focus{ color: black; font-weight: bold; } /* =========================== page: iterations =========================== */ .courseList{ display: flex; flex-direction: column; } .courseList > li{ margin-left: 50px; } /* =========================== page: forms =========================== */ #form, #form2{ display: grid; grid-template-rows: 1fr 1fr; gap: 10px; } label{ display: block; } input{ width: 100%; } #send, #send2{ margin-bottom: 20px; } /* =========================== page: fileread =========================== */ /* =========================== MEDIA QUERIES =========================== */ @media only screen and (min-width: 1350px){ #nav-ul{ justify-content: center; align-content: center; } } @media only screen and (max-width : 600px){ #nav-ul{ justify-content: center; align-content: center; } .menu-btn{ padding: 10px; margin: 5px; width: 175px; } h1{ font-size: 2rem; } span, i, p{ text-align: justify; } }

Dold text

p-calculate.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=4; ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <div class="uppgifts-div"> <h3>Beräkna arean</h3> <?php // Kolla att Skicka knapp klickats if(isset($_POST['skicka2'])){ // Kolla att BÅDA fält matats in if(!empty($_POST['langd']) && !empty($_POST['bredd'])){ $langd = $_POST['langd']; $bredd = $_POST['langd']; echo "Längden $langd meter och bredden $bredd meter ger en area på " . $langd*$bredd . " kvadratmeter."; } // Annars skriv ut uppmaning om att mata in båda fält. else{ echo "<span style='color: red; font-size:1rem;'>Både längd och bredd måste anges!</span>"; } echo "<br><br><a href='p-forms.php' class='backA' style='display:block; text-align:center;'>Gå tillbaks till föregående sida</a>"; } ?> </div> <?php include("include/c-footer.php");?>

Dold text

p-conditions.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=2; ?> <title><?= pageTitle("Villkor"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 2. Villkor</h2> <div class="uppgifts-div"> <h3>Datum/klockslag: ÅÅÅÅ-MM-DD:TT.MM</h3> <?php $dateNow = date('Y-m-d') . ':' . date('H.i'); echo "<p style='text-align: center;'>Datum/klockslag: $dateNow<br><br></p>"; ?> <h3>Idag är det (inte) söndag</h3> <p style='text-align: center;'>Idag är det <?php $isSunday = date('D'); echo $isSunday === 'Sun' ? ' söndag' : ' inte söndag'; ?>.<br><br></p> <h3>Det är morgon/förmiddag/eftermiddag eller kväll/natt</h3> <p style='text-align: center;'>Det är <?php // Gammalt variabelnamn från JS-Intro-kursen - gammal favorit! $getHM = date('H:i'); if($getHM >= '06:00' && $getHM <= '08:59'){ echo 'morgon'; } else if($getHM >= "09:00" && $getHM <= '11:59'){ echo 'förmiddag'; } else if($getHM >= '12:00' && $getHM <= '17:59'){ echo 'eftermiddag'; } else if($getHM >= '18:00' && $getHM <= '05:59'){ echo 'kväll/natt'; } ?>.</p> <h3>Idag är det <i>Veckodag</i></h3> <p style='text-align: center;'>Idag är det <?php $weekDay = date('D'); switch($weekDay){ case 'Mon': echo 'måndag'; break; case 'Tue': echo 'tisdag'; break; case 'Wed': echo 'onsdag'; break; case 'Thu': echo 'torsdag'; break; case 'Fri': echo 'fredag'; break; case 'Sat': echo 'lördag'; break; case 'Sun': echo 'söndag'; break; }?>.</p><br> <?php echo nextPage('p-iterations','3. Upprepningar');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-fileread.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=5; ?> <title><?= pageTitle("5. Filinläsning"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 5. Filinläsning</h2> <div class="uppgifts-div"> <h3>Inläsning av extern textfil</h3> <?php // Om filen ej existerar. if(!file_exists('courses.txt')){ echo "Filen kunde inte hittas!"; } // Öppnna annars för inläsning ('r') else{ echo "Filen finns. Öppnar den nu och skriver ut som en punktlista...<br><br>"; $fp = fopen('courses.txt','r'); // Skriver ut <ul>-element echo "<ul style='margin-left: 30px;'>"; // feof = slutet på filen så betyder "så länge INTE // slutet på filen har nåtts för öppnad fil $fp så..." while(!feof($fp)){ echo "<li>" . fgets($fp) . "</li>"; } // Avsluta punktlistan, skapa luft nedanför innan nästa länk echo "</ul><br>"; }?> <?php echo nextPage('index','0. Startsida/Frågor');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-forms.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=4; ?> <title><?= pageTitle("4. Formulär"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 4. Formulär</h2> <div class="uppgifts-div"> <h3>Del 1 - Skicka data med GET</h3> <?php // Kolla att Skicka knapp klickats if(isset($_GET['skicka1'])){ // Kolla att BÅDA fält matats in if(!empty($_GET['fname']) && !empty($_GET['ename'])){ $fornamn = $_GET['fname']; $efternamn = $_GET['ename']; // Skriv ut för -& efternamn echo "Hej " . $fornamn . " " . $efternamn . "!"; } // Annars skriv ut uppmaning om att mata in båda fält. else{ echo "<span style='color: red; font-size:1rem;'>Du måste ange både för- och efternamn!</span>"; } } ?> <form id="form" action="p-forms.php" method="GET"> <div> <label for="fnamn">Förnamn:</label> <input id="fnamn" type="text" name="fname"> </div> <div> <label for="enamn">Efternamn:</label> <input id="enamn" type="text" name="ename"> </div> <input type="submit" name="skicka1" value="Skicka" id="send"> </form> <h3>Del 2 - Skicka data med POST</h3> <form id="form2" action="p-calculate.php" method="POST"> <div> <p style="font-size: 1rem; margin-bottom: 5px;">Beräkna arean på en yta genom att ange längd och bredd.</p> <label for="langd">Längd:</label> <input id="langd" type="number" name="langd"> </div> <div> <label for="bredd">Bredd:</label> <input id="bredd" type="number" name="bredd"> </div> <input type="submit" value="Skicka" name="skicka2" id="send2"> </form> <?php echo nextPage('p-fileread','5. Filinläsning');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-iterations.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=3; ?> <title><?= pageTitle("3. Upprepningar"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 3. Upprepningar</h2> <div class="uppgifts-div"> <h3>Del 1</h3> <p style='text-align: center;'> <?php for($i = 10; $i>0; $i--){ echo $i . '<br>'; } ?> </p><br> <h3>Del 2</h3> <p style="margin-bottom: 10px; text-align: center;">Kurslistan i den ordning kurserna ges:</p> <?php $arrCourses = array( "Webbutveckling I","Introduktion till programmering med JavaScript", "Grafisk teknik för webb för webb","Webbanvändbarhet","Webbutveckling II","Databaser", "Webbdesign för CMS","Webbutveckling III" ); // Skriv ut echo '<ul class="courseList">'; foreach($arrCourses as $arrCourse){ echo "<li>$arrCourse</li>"; } echo '</ul>'; ?><br> <h3>Del 3</h3> <p style="margin-bottom: 10px; text-align: center;">Kurslistan i bokstavsordning:</p> <?php // Sortera och skriv ut sort($arrCourses); echo '<ul class="courseList">'; foreach($arrCourses as $arrCourse){ echo "<li>$arrCourse</li>"; } echo '</ul>'; ?><br> <?php echo nextPage('p-forms','4. Formulär');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-questions.php

<h2>Moment 2 - Frågor</h2> <ul> <li class="questions"> <strong>Har du tidigare erfarenhet av utveckling med PHP?</strong> <br><p>Nej, inte i denna utsträckning. Jag har för länge sedan kikat på det men förvirrats av dess sätt att användas på som exempelvis att skriva den direkt inuti HTML-kod. För säkert 10 år sedan öppnade jag upp diverse Wordpress-php-filer och förstod så klart ingenting.<br><br>Det blir roligt att öppna om samma Wordpress-filer efter CMS-kursen som då fokuserar på just Wordpress om jag minns rätt</p> </li> <li class="questions"> <strong>Beskriv kortfattat vad du upplever är fördelarna med att använda PHP för att skapa webbplatser.</strong> <br><p>Två starka fördelar:<br><br>1) Den första är att kunna skapa mer dynamiska webbplatser snabbare tack vare det modulära tänket vilket jag älskar. Jag hoppas vi aldrig går tillbaka till "vanilla HTML" på det viset utan får hålla på med både modulär php, javascript, och så vidare. C# är ju objektorienterat och OOP anammar ju delvis det där modulära tänket som jag rent av älskar!<br><br>2) Den andra stora fördelen är att kunna skapa säkra webbplatser tack vare att php-koden döljs undan. Jag upptäckte även att kommentarer inuti php döljs men om man skriver HTML-kommentarer precis utanför php-koden så syns de slags kommentarerna.</p> </li> <li class="questions"> <strong>Hur har du valt att strukturera upp dina filer och kataloger?</strong> <br><p>Jag har <i>index.php</i> i "webbserverns" rotkatalog. I mappen <i>include</i> så finns filer med prefix i filnamnen för att lättare veta vad som syftar på vad: "c" = component/komponent till en viss sida, "f" = funktion till en viss sida.<br><br>Notera således att funktionerna i <i>config.php</i> är medvetet globala då den filen ändå inkluderas på alla sidor. En första tanke var dock katalogerna <i>page</i> för sidorna (det vill säga, "p" = page/sida) och <i>function</i> för funktionerna.<br><br>Men det skippade jag då jag har haft problem med att länka rätt mellan filer som ligger i olika nivåer i olika kataloger än så länge.</p> </li> <li class="questions"> <strong>Har du följt guiden, eller skapat på egen hand?</strong> <br><p>Jag har delvis följt guiden för den grundläggande strukturen sedd i <i>index.php</i>. Då var det bara att kopiera och klistra in för alla undersidor och inkludera nödvändig php-kod där för att lösa respektive uppgift.</p> </li> <li class="questions"> <strong>Har du gjort några förbättringar eller vidareutvecklingar av guiden (om du följt denna)?</strong> <br><p>Vad som kan tolkas som förbättringar jag har gjort är i form av funktioner som kan anropas. <br><br>Exempelvis så sätts ett värde för vilken sida man är på vilket sedan anropas i <i>c-header.php</i> för att markera vilken nuvarande sida är. Detta syns <i>$currentBtn</i>-variabeln i början av undersidorna. <br><br>Annan funktion är den som anropas inuti varje <i>title</i>-element för att skriva ut undersidans namn efter webbplatsens namn. <br><br>En ytterligare funktion är den som anropas i slutet på varje uppgift: <i>nextPage</i> som returnar länknamn (ej filändelse) och strängen inom parentesen. Medför snabbare navigering. <br><br>Funktionerna finns i <i>config.php</i> då de används på varje webbsida, exklusive <i>nextPage</i> på denna sida.</p> </li> <li class="questions"> <strong>Vilken utvecklingsmiljö har du använt för att genomföra uppgiften (editor, webbserver-paket (Xampp, Lamp, Wamp eller liknande)?</strong> <br><p>Jag har använt mig av VSCode som huvudsaklig utvecklingsmiljö. Jag har använt mig av XAMPP som webbserver för PHP.<br><br>Installerade även <i>Apache</i> direkt som Service i Windows 10 för att slippa behöva starta XAMPP manuellt varje gång jag startar datorn.</p> </li> <li class="questions"> <strong>Har något varit svårt med denna uppgift?</strong> <br><p>Ja, CSS som vanligt. En sak jag inte förstod var varför jag fick felmeddelande när jag valde att inkludera typsnitt lokalt, alltså med <i>@font-face</i>.<br><br>Då stod det i <i>Console</i> att <i>"download failed font"</i> (trots att jag såg att sökvägen till filen var rätt med kataloger och filnamn).<br><br>Så jag fick använda direktlänk till Google Fonts för det futuristiska typsnittet på denna webbplats. Jag vet inte om jag hade valt fel slags varianter av typsnitten (det var .tff) eller om det är något php och typsnitt jag inte begriper mig ännu på.</p> </li> </ul>

Dold text

p-variables.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=1; ?> <title><?= pageTitle("1. Variabler"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 1. Variabler</h2> <div class="uppgifts-div"> <h3>Enskilda</h3> <?php // Skapar variabler $name = "WebbKodsLärlingen"; $age = "33"; $email = "WKL@yolo.com"; echo "<ul class='variabel-ul'><li>$name</li><li>$age</li><li>$email</li></ul><br>"; ?> <h3>Kombinerade</h3> <?php echo "<p class='variabelP'>Hej! Jag heter $name" . ", är " . $age . " år gammal och nås på följande e-post: " . "<a href='mailto:$email'>$email</a>.</p><br>"; echo nextPage('p-conditions','2. Villkor'); ?> </div> <?php include("include/c-footer.php");?>

Dold text

include/c-footer.php

</main> <footer> <span id="footer-text">© 2023 WebbKodsLärlingen<br> VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans) - Moment 2</span> </footer> </div> </body> </html>

Dold text

include/c-header.php

</head> <body> <div class="main-layout"> <div> <header> <h1>FRAMTIDSBLOGGEN I PHP MOMENT 2</h1> </header>

Dold text

include/c-nav.php

<nav> <ul id="nav-ul"> <li class="menu-btn <?php echo setCurrentBtn(0); ?>">0. <a href="index.php"> Startsida </a> </li> <li class="menu-btn <?php echo setCurrentBtn(1); ?>">1. <a href="p-variables.php"> Variabler </a> </li> <li class="menu-btn <?php echo setCurrentBtn(2); ?>">2. <a href="p-conditions.php"> Villkor </a> </li> <li class="menu-btn <?php echo setCurrentBtn(3); ?>">3. <a href="p-iterations.php"> Upprepningar </a> </li> <li class="menu-btn <?php echo setCurrentBtn(4); ?>">4. <a href="p-forms.php"> Formulär </a> </li> <li class="menu-btn <?php echo setCurrentBtn(5); ?>">5. <a href="p-fileread.php"> Filinläsning </a> </li> </ul> </nav> </div> <main>

Dold text

include/config.php

<?php // PHP-kod konfiguration innan någon faktisk HTML ens laddas först // Funktion som ger extra CSS-klass så att vald menyknapp får grön box-shadow function setCurrentBtn($btnNumber){ return $GLOBALS['currentBtn']==$btnNumber ? 'current-btn' : ''; } // Funktion som skriver ut anropad sträng i <title>-element function pageTitle($pTitle){ return "FRAMTIDSBLOGGEN i PHP Moment 2 | " . $pTitle; } // Funktion som länkar till nästa uppgiftslösning (undersida) function nextPage($nPage, $pName){ return "<p style='text-align: center;' class='variabelP'><a href='$nPage.php'>Nästa uppgift ($pName)</a></p>"; } // Här slutar PHP-koden och HTML laddas in. Dessa kommentarer syns ej i webbläsaren! :-) ?> <!DOCTYPE html> <html lang="sv"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/styles.css"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;80..." rel="stylesheet">

Dold text

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Nu vet jag inte vad exakt uppgiften är, men skulder, förseningar, svartlistade och så vidare

Sen är det inte så enkelt med förnamn och efternamn, i asien är det vanligt att man säger ’Larsson Mikael’, dvs att familjenamnet kommer först

Sen kan jag tycka att man kan återanvända ett adressobjekt till alla delar istället för separata

Nu ska det också sägas att jag inte själv gjort exakt sådana diagram, så kanske är att jag missförstått något

Regissören har väl också ett namn, adresser kan innehålla land eller delstat och sånt, …

Går att göra mycket mer, som att tänka på underhåll och vad som händer om man vill ändra något i framtiden

Permalänk
Skrivet av medbor:

Nu vet jag inte vad exakt uppgiften är, men skulder, förseningar, svartlistade och så vidare

Sen är det inte så enkelt med förnamn och efternamn, i asien är det vanligt att man säger ’Larsson Mikael’, dvs att familjenamnet kommer först

Sen kan jag tycka att man kan återanvända ett adressobjekt till alla delar istället för separata

Nu ska det också sägas att jag inte själv gjort exakt sådana diagram, så kanske är att jag missförstått något

Regissören har väl också ett namn, adresser kan innehålla land eller delstat och sånt, …

Går att göra mycket mer, som att tänka på underhåll och vad som händer om man vill ändra något i framtiden

Labbuppgiften handlar om att göra ett ER-diagram utifrån en verksamhetsbeskrivning och det står i uppgiftsbeskrivningen under mål att "Ditt ER skall vara en grund till enfungerade database i ett senare designsteg därför skall alla egna rimliga antaganden motiveras på etttydligt sätt".

Jag som nybörjare inom Databaser kommer ju knappast att kunna dra samma "rimliga antaganden" som läraren. Vi har inte ens börjat med att översätta dessa till vanliga UML-tabeller(?). Det är vid nästa steg så kanske det finns lite överseende över detta från läraren!

Verksamhetsbeskrivning (skärmdump av PDF då texten ej formatterar korrekt vid copy&paste)

Dold text

I Verksamhetsbeskrivningen ovan markerade jag då potentiella entiter, dess attribut, primärnycklar (en per entitet max) och potentiella svaga entiteter, sekundärnycklar (foreign keys(?)), samt diverse multivalue attribut, derived attributes, med mera!

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Uppdaterad inlämnad Labb 1 i Databaser

Jag har uppdaterat min inlämnade Labb 1 i Databaser-kursen:

Nu upplever jag att den verkar använda databasbegreppen mer korrekt. Samtidigt förvirras jag av faktumet i läroboken att dessa "scheman" egentligen inte nödvändigtvis måste översättas till exakta "Tabeller" utan kan snarare bara ses som en överskådlig modell(?).

Jag är glad att jag inte ska bli databastekniker i allafall!

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Nu upplever jag att den verkar använda databasbegreppen mer korrekt. Samtidigt förvirras jag av faktumet i läroboken att dessa "scheman" egentligen inte nödvändigtvis måste översättas till exakta "Tabeller" utan kan snarare bara ses som en överskådlig modell(?).

Jag är glad att jag inte ska bli databastekniker i allafall!

Databasdesign är svårt. Och kul. Och nära besläktat med objekt-orienterad design.

ER-diagram kan var ett bra arbetsverktyg i början av designen. Personligen föredrar jag code-first eller database-first och generera det man inte skapar (databas och ER-diagram genereras från koden till exempel). Det är oftast ett rejält misstag att inte se hur koden kommer fungera mot databasen i ett tidigt skede, man vill slippa att sitta och skriva idiot-kod som ska kompensera något designmisstag i databasen.

Några lösa tankar om ditt ER-diagram:
1) Det finns ett flertal olika personer och adresser. Kan inte dessa vara egna entiteter, som implementeras som en enda tabell som har flera olika användningsområden? Är en chef en anställd? Kan en anställd hyra film? Kan en skådelspelare regissera?
2) Du vill definitivt undvika "arrayer", dvs att lagra flera strängar i ett och samma fält i en tabell.

Det känns som om du inte har någon speciell insikt i objekt-orienterad programmering (än). Jag menar att det är en rejäl brist om man ska skriva den här typen av applikation. Jag kan ha fel, men det är vad jag läser från dina arrayer-av-strängar-i-arrayer-texter. Hur hade du designat samma datamodell som klasser? Notera att arv inte finns explicit i en SQL-databas...

Det känns inte heller som om ni har pratat om normalformer i databasdesign. Du kommer få lära dig den hårda vägen att allt annat än (>=) tredje normalformen suger när det ska hanteras av koden. Jag föreslår att du ägnar några timmar åt att förstå poängen med normalisering (för det ser inte ut som du gjort det, än).

Jag tror du bör tänka över vad en film är för något. Är det den fysiska mediat man hyr ut (jag antar att vi befinner oss i en tid då man fortfarande gjorde så) eller är det informationen som man kan läsa om på IMDB? Jag hävdar att dessa två fenomen bör modelleras separat. Hur håller man reda på om en viss film-individ är uthyrd eller inte? Vem är den uthyrd till?

Ovanstående är inte menat att vara elakt eller få dig att tycka att ämnet är tråkigt (för det är det inte). Det är en uppmaning till att tänka ett par varv till och ifrågasätta din modellering. Om du ska jobba i branchen är det inte direkt sista gången du kommer behöva göra det... Det är mer regel än undantag att verkligheten/kraven ändrar sig. Då är det jäkligt skönt att man inte latade sig och designade den ursprungliga modellen till att vara så flexibel som möjligt. Den observationen gäller inte bara databaser.

Permalänk
Skrivet av KAD:

Databasdesign är svårt. Och kul. Och nära besläktat med objekt-orienterad design.

ER-diagram kan var ett bra arbetsverktyg i början av designen. Personligen föredrar jag code-first eller database-first och generera det man inte skapar (databas och ER-diagram genereras från koden till exempel). Det är oftast ett rejält misstag att inte se hur koden kommer fungera mot databasen i ett tidigt skede, man vill slippa att sitta och skriva idiot-kod som ska kompensera något designmisstag i databasen.

Några lösa tankar om ditt ER-diagram:
1) Det finns ett flertal olika personer och adresser. Kan inte dessa vara egna entiteter, som implementeras som en enda tabell som har flera olika användningsområden? Är en chef en anställd? Kan en anställd hyra film? Kan en skådelspelare regissera?
2) Du vill definitivt undvika "arrayer", dvs att lagra flera strängar i ett och samma fält i en tabell.

Det känns som om du inte har någon speciell insikt i objekt-orienterad programmering (än). Jag menar att det är en rejäl brist om man ska skriva den här typen av applikation. Jag kan ha fel, men det är vad jag läser från dina arrayer-av-strängar-i-arrayer-texter. Hur hade du designat samma datamodell som klasser? Notera att arv inte finns explicit i en SQL-databas...

Det känns inte heller som om ni har pratat om normalformer i databasdesign. Du kommer få lära dig den hårda vägen att allt annat än (>=) tredje normalformen suger när det ska hanteras av koden. Jag föreslår att du ägnar några timmar åt att förstå poängen med normalisering (för det ser inte ut som du gjort det, än).

Jag tror du bör tänka över vad en film är för något. Är det den fysiska mediat man hyr ut (jag antar att vi befinner oss i en tid då man fortfarande gjorde så) eller är det informationen som man kan läsa om på IMDB? Jag hävdar att dessa två fenomen bör modelleras separat. Hur håller man reda på om en viss film-individ är uthyrd eller inte? Vem är den uthyrd till?

Ovanstående är inte menat att vara elakt eller få dig att tycka att ämnet är tråkigt (för det är det inte). Det är en uppmaning till att tänka ett par varv till och ifrågasätta din modellering. Om du ska jobba i branchen är det inte direkt sista gången du kommer behöva göra det... Det är mer regel än undantag att verkligheten/kraven ändrar sig. Då är det jäkligt skönt att man inte latade sig och designade den ursprungliga modellen till att vara så flexibel som möjligt. Den observationen gäller inte bara databaser.

Tack så mycket för det mustiga svaret!

I Webbutveckling II (andra kursen) kommer vi in på OOP i Moment 3. Denna vecka med andra ord.

I Databaser kommer vi in på Normalisering inom kort (se schema nedan). Om jag förstått det korrekt så handlar det om att designa databasmodellen så att man inte råkar försöka lagra samma slags data fler än en gång?

Här är de 2 andra labbarna om du vill se vad de handlar om:

Du behöver absolut inte säga om hur jag ska lösa dem. Du får gärna berätta dock om du upplever att labbarna "lär en väsentliga praktiska verktyg i praktiken vid databashantering" eller om det är lite "onödigt kunnande" i jämförelse med hur verkligheten ser ut när man väl ska arbeta med databaser i skarpt läge!

Under introduktionskursen i Javascript så berörde vi OOP inte mer än att kunna använda JSON. Vi har inte lärt oss något om klasser med ska tydligen komma nu under php-kursens tredje moment!

Som jag tolkar Labben så är det hypotetiska företaget ett uthyrningsföretag som erbjuder filmer i fysiskt medium, varav entiteter och attribut som Lager-entitet med attribut som Hyllnummer och Antal kopior per film. Projektuppgiften i denna kurs är mer "allt-i-ett" vad gäller designen av ER-modell, normalisering, med mera.

Labb 1 är mer bara att komma igång med en grundläggande ER-modell som jag tolkar det.

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Kompletteringskrav på första labben i Databaser-kursen

Jag har fått kompletteringskrav (Fx) i första labben i Databaser-kursen:

Nu blir jag också lite orolig för läraren verkar ha gett återkoppling på en helt annan ER-modell än min (jag har korrigerat återkoppling nr 4 redan samt fixat 1:N kardinalitet för Sambandstypen "hyr_ut_till"):

Jag vet inte om Läraren syftar på min kommentar "Butik genomför 'M' uthyrningar..." och där har jag ändrat nu så att 1 Butik hyr ut N antal filmer till medlemmar. Det skulle göra mig riktigt förbannad om jag misslyckas denna kurs på grund av en oaktsam lärare. Exempelvis kommentar 1) nämner läraren om "Hyr" och "Uthyrning" men jag har inga sådana utan närmast är Sambandstypen hyr_ut_till med ett attribut som skulle kunna tolkas som ett nyckelattribut (Hyrnummer).

I andra försöket jag skickar in så får jag namnge användarnamn, labbnummer, och försöksnummer - typ databasnamngivning?

Jag blir också smått frustrerad över att jag egentligen inte riktigt förstår vad Entitet, Sambandstyp, "nyckel attribut" syftar på i praktiken (i en färdig databas med MySQL exempelvis). Jag antar att Entitet är tabellnamnet och primärnyckel (t.ex. det understreckade attributet Butiks-nr) är det som numrerar vilken Butik varje "attributkolumn" syftar på i samma tabell?

Men vad innebär Sambandstyp mellan olika Entiteter/Tabellnamn(?) Innebär det hur många Tabeller det finns av ett slag i förhållande till ett annat, varav 1:1, 1:N, och N:M kardinaliteterna?

Så i tabellen "Butik" så är det Butiks-nr som gör att alla övriga attribut gäller för varje Butiks-nr. Alltså, Butiks-nr 1 har attributet Telefon-nr med ett givet attributvärde, och så vidare, medan Butiks-nr 2 har andra attributvärden för samma attribut Telefon-nr, och så vidare?

Obligatorisk kurslitteratur i kursen är: Thomas Padron-McCarthy & Tore Risch (2018) - Databasteknik.

Smått frustrerande än så länge men det går nog vägen!

På återseende!

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Jag har fått kompletteringskrav (Fx) i första labben i Databaser-kursen:
<Uppladdad bildlänk>

Nu blir jag också lite orolig för läraren verkar ha gett återkoppling på en helt annan ER-modell än min (jag har korrigerat återkoppling nr 4 redan samt fixat 1:N kardinalitet för Sambandstypen "hyr_ut_till"):
<Uppladdad bildlänk>

Jag vet inte om Läraren syftar på min kommentar "Butik genomför 'M' uthyrningar..." och där har jag ändrat nu så att 1 Butik hyr ut N antal filmer till medlemmar. Det skulle göra mig riktigt förbannad om jag misslyckas denna kurs på grund av en oaktsam lärare. Exempelvis kommentar 1) nämner läraren om "Hyr" och "Uthyrning" men jag har inga sådana utan närmast är Sambandstypen hyr_ut_till med ett attribut som skulle kunna tolkas som ett nyckelattribut (Hyrnummer).

I andra försöket jag skickar in så får jag namnge användarnamn, labbnummer, och försöksnummer - typ databasnamngivning?

Jag blir också smått frustrerad över att jag egentligen inte riktigt förstår vad Entitet, Sambandstyp, "nyckel attribut" syftar på i praktiken (i en färdig databas med MySQL exempelvis). Jag antar att Entitet är tabellnamnet och primärnyckel (t.ex. det understreckade attributet Butiks-nr) är det som numrerar vilken Butik varje "attributkolumn" syftar på i samma tabell?

Men vad innebär Sambandstyp mellan olika Entiteter/Tabellnamn(?) Innebär det hur många Tabeller det finns av ett slag i förhållande till ett annat, varav 1:1, 1:N, och N:M kardinaliteterna?

Så i tabellen "Butik" så är det Butiks-nr som gör att alla övriga attribut gäller för varje Butiks-nr. Alltså, Butiks-nr 1 har attributet Telefon-nr med ett givet attributvärde, och så vidare, medan Butiks-nr 2 har andra attributvärden för samma attribut Telefon-nr, och så vidare?

Obligatorisk kurslitteratur i kursen är: Thomas Padron-McCarthy & Tore Risch (2018) - Databasteknik.

Smått frustrerande än så länge men det går nog vägen!

På återseende!

Mvh,
WKL.

Det handlar ju om liknande som du fick svar på här

Varje butik har ju ett eget lager met ett visst antal filmindivider av varje film

Det måste finnas länkar mellan filmindivider, medlemmar, lager, så att butiken kan se exakt vilka filmindivider som är utlånade till vem och från vilken butik

Men precis som du säger har läraren antagligen råkat skriva någon kommentar som inte matchar perfekt på din bild

Permalänk

Nytt försök Labb1 i Databaser-kursen

Jag har gjort ett nytt försök i Labb1 i Databaser-kursen efter att ha tittat på lektion om ER-modell till basrelationer (preliminära tabeller):

Det "kopplade" för mig syftet med Sambandstyper och hur N:M (flera-mot-flera) innebär en enskild tabell och då la jag Hyrnummer, Återlämningsdatum och Uthyrningsdatum där. Det blir då också kopplat mellan Medlem och Film vilket är två relevanta tabeller att ha PK & FK relaterade.

Det enda jag undrar är om attributen Hyllnummer, Filmkopior_per_film och Hyrpris bör vara Multivalue Attribute istället? För sådana slags attribut innebär enskilda tabeller för annars verkar det ju som om Sambandstypen inte blir rätt? Och nu när jag kollar så ser jag ju N:M för som_lagrar vilket innebär en enskild tabell.

Men att läsa från M Filmer som_lagras i N Lager låter ju knasigt? För 1 Butik har 1 Lager så M Filmer kan väl inte lagras i N Lager? Filmer kan så klart lagras i flera olika Lager men jag har ju också sagt i ER-modellen att en butik kan som högst ha ett lager ju. Hm. Tänker jag rätt här nu med ER-modelleringen?

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Skrivet av medbor:

Det handlar ju om liknande som du fick svar på här

Varje butik har ju ett eget lager met ett visst antal filmindivider av varje film

Det måste finnas länkar mellan filmindivider, medlemmar, lager, så att butiken kan se exakt vilka filmindivider som är utlånade till vem och från vilken butik

Men precis som du säger har läraren antagligen råkat skriva någon kommentar som inte matchar perfekt på din bild

Jag har nu korrigerat efter viss vägledning från lärare då läraren idag hade 1:1-sessioner via Zoom med alla som deltog där.

I mitt ovanstående inlägg funderar jag på om Lager N: <som_lagrar> M Film är korrekt. För om en butik har ett Lager så blir väl N efter Lager att det skulle vara som 1 Butik har N antal Lager?

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Godkänd Labb 1 i Databaskursen och Objektorienterad PHP är roligt men snurrigt!

Godkänd Labb 1 i Databaskursen och Objektorienterad PHP är roligt men snurrigt!
Jag har äntligen blivit godkänd i Labb 1 i Databaskursen där återkopplingen blev: "ok". Flera andra studenter hade också fått samma matnyttiga återkoppling: "Ok".

Nu har jag börjat med Objektorienterad PHP och jag har då följande klassfil som autoinkluderas med hjälp spl-funktion inlagd i en config.php-fil:
Todo.class.php

<?php // Klassnamn class Todo { // Privata variabler som bara nås via klassens metoder // Sträng för 'GET' eller 'POST' private $methodType; // Sträng för name-attributvärdet för <input>-element private $inputName; // Sträng för submit-knappens (<input type="submit">) name-attribut private $btnName; // Sträng för inmatningsruta för att lägga till med knappen 'Lägg till' private $todoString; // Heltal för antal tecken som måste matas in (visas i felmeddelande - se klassmetod showErrorMsg()) private $errorLenNumber; // Hantering av JSON-fil private $jsonFile; // Klassmetoder // Klassmetod för att sätta formulärmetod (GET eller POST) function setFormMethod($a) : string{ return $this->methodType = $a; } // Klassmetod för att välja JSON-fil att arbeta med function setJSON($a) : string{ return $this->jsonFile = $a; } // Klassetod för att hämta data från vald JSON-fil function getJSON(){ if(file_exists($this->jsonFile)){ if(fgets(fopen($this->jsonFile,'r')) === '[]'){ } else{ return file_get_contents($this->jsonFile); } } else{ file_put_contents($this->jsonFile,'[]'); } } // Klassetosd för att skriva till vald JSON-fil function writeJSON($a) : string{ return file_put_contents($this->jsonFile,$a); } function emptyJSON(){ return file_put_contents($this->jsonFile,'[]'); } // Klassmetod för att sätta name-attribut för <input>-element function setInput($a) : string{ return $this->inputName = $a; } // Klassmetod för att sätta name-attribut för <input type="submit">-element function setButton($a) : string{ return $this->btnName = $a; } // Klassmetod för att sätta Formetod, inmatningsfält och submit-knapp samtidigt när klass initieras function __construct(string $method, string $input, string $btn, string $json){ $this->methodType = $method; $this->inputName = $input; $this->btnName = $btn; $this->jsonFile = $json; } // Klassmetod som returnerar uttryck av php-metod (GET eller POST) att använda i if-satser // för att kontrollera om en knapp (definierad med setButton-metoden) har klickats på function getCheckAddClick(){ if($this->methodType === 'GET' && $this->btnName !== ''){ return isset($_GET[$this->btnName]); } if($this->methodType === 'POST' && $this->btnName !== ''){ return isset($_POST[$this->btnName]); } } // Klassmetod som returnerar uttryck vald stränglängd($a) (GET eller POST) function getCheckInputLength($a) : int{ if($this->methodType === 'GET' && $this->inputName !== ''){ $this->errorLenNumber = $a; // Sätt antal tecken (används av ShowErrorMsg()) return strlen($_GET[$this->inputName]) < $a; } if($this->methodType === 'POST' && $this->inputName !== ''){ $this->errorLenNumber = $a; // Sätt antal tecken (används av ShowErrorMsg()) return strlen($_POST[$this->inputName]) < $a; } } // Klassmetod som returnerar felmeddelande och minst antal tecken ($errorLenNumber) som måste matas in. function showErrorMsg(){ echo "<p id='todoErrorMsg'>Ange minst " . $this->errorLenNumber . " tecken för att lägga till sak att göra i listan nedan.</p>"; } function writeTodoLine($a){ echo "<div class='todo-line'><p>$a</p><button>Klar</button></div>"; } } ?> <!-- $add = 'addTodo'; // Kolla om Lägg till klickats if(isset($_POST[$add])){ // Visa felmeddelande när stränglängd är lägre än 5 tecken if(strlen($_POST['todoItem']) < 5){ echo "<p id='todoErrorMsg'>Du måste ange minst fem tecken för att lägga till i listan nedan.</p>"; } } -->

Dold text

moment3.php (slutrenderad HTML härifrån)

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=6; // Initierar klass "Todo" (se även tillagd autoinkludering i config.php) och dess konstruktör med tre strängar: // (formulärmetod, name-attributvärde för inmatningsfält & name-attributvärde för Lägga till-knapp) ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 3 - Att göra lista med PHP</h2> <div class="uppgifts-div"> <h3>En "Att göra"-lista med objektorienterad PHP</h3> <form id="form3" action="p-moment3.php" method="POST"> <label for="todoInput">Ange och lägg till ny sak att göra:</label> <input id="todoInput" type="text" name="todoItem" placeholder="Sak att göra..."> <?php // PHP-KOD som körs mellan inmatningsfält och "Lägg till"-knappen // Klasmetod som kontrollerar om "Lägg till"-klickats på $addtodo = new Todo('POST','todoItem','addTodo', 'todolist.json'); if($addtodo->getCheckAddClick()){ // Klassmetod som kontrollerar om färre än valt antal tecken har skrivits (5 i detta fall) if($addtodo->getCheckInputLength(5)){ // Klassmetod som annars skriver ut felmeddelande $addtodo->ShowErrorMsg(); }} ?> <div id="todoButtons"> <input type="submit" name="addTodo" value="Lägg till" id="todoBtn"> <input type="submit" name="removeTodos" value="Rensa" id="clearBtn"> </div> </form> <div class="todoList"> <h3>Saker att göra</h3> <?php // PHP-KOD som körs nedanför inmatningsfältet och knapparna, här visas Att göra listan // Här skrivs Att göra-listan ut och klickad "Klar" via GET tas bort. $addtodo->setJSON('todolist.json'); // Klasmetod som kontrollerar om "Lägg till"-klickats på if($addtodo->getCheckAddClick()){ // Klassmetod som kontrollerar om minst valt antal tecken har skrivits (5 i detta fall) if(!$addtodo->getCheckInputLength(5)){ // Kör i sådana fall dessa metoder från klassinstansen $test = $addtodo->getJSON(); }} // $jsonData = json_encode($test); // $addtodo->writeJSON($jsonData); ?> </div> </div> <?php include("include/c-footer.php");?>

Dold text

Momentet handlar om att skapa en "Att göra"-lista där du lägger till i en array som lagras i en JSON-fil lokalt på webbservern. Varje enskild Att göra-sak som läggs till ska få en knapp/ikon som du kan klicka/trycka på för att då ta bort den och på så vis ladda om arrayen på nytt.

I uppgiftsbeskrivningen anges följande krav:
Din lösning på uppgiften ska vara objektorienterad. Metoder och egenskaper för "att göra"-listan ska ligga i en eller flera klasser. Funktionalitet som minst ska skötas i en eller flera klasser:

  • hantering av JSON-fil (läsa in och spara till filen),

  • kontroller av inmatning från formulär (Set-metoder),

  • Get-metod [syftar på Getters & Setters, inte $_GET] för att returnera "att göra"-listan,

  • borttagning av en "att göra",

  • rensning av hela "att göra"-listan,

Som det går att se så har jag just nu allt i en och samma class.php-fil. Men jag undrar om det är fel väg att gå då det behövs köras php-kod på två olika ställen: 1) Visa felkod om du inte matar in minst 5 tecken för saken att göra, och 2) Läsa in nuvarande lista (eller skapa en tom JSON-fil om filen saknas) samt uppdatera om man hade klickat på "Klar" på en inlagd sak (den skickas med GET-metoden med ett unikt ID som lösningsförslag).

Exemplen under genomgången av Objektorienterad PHP så fick vi se hur man lade in kattnamn och kattålder i en klass Cat och skrev ut med echo $this->catname; och det tyckte jag var rätt så magert. Jag har skapat tråd och frågat lite mer om var någonstans de egentligen vill ha diverse kod för jag förvirras över hur omständigt det känns att göra det objektorientierat än att bara ha den logik där det behövs på "HTML"-sidan!

Vad tycks?: Ha flera olika klassfiler som fokuserar på mindre saker istället för en och samma klassfil med alla nödvändiga klassmetoder? Jag har en klassinstansiering med fyra strängar (se $addtodo = new Todo(...);) som skickas för att objekten ska kunna arbeta dynamiskt istället för att redan innehålla färdigbestämda data. Annars försvinner väl modularitetstänket där?

I Moment3.php har jag klassinstansiering och sedan metodanrop inuti diverse if-satser. Är detta ens rätt användning av Objektorienterad PHP eller bör jag endast ha returvärden från diverse klassmetodanrop som tagit emot en eller flera argument vid anropen?

Ge gärna förslag på struktureringen och peka gärna ut nybörjarmisstag med hela OOP-tänket om du vill!

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Glädjande framsteg med Objektorienterad PHP

Glädjande framsteg med Objektorienterad PHP
Nu har jag äntligen lyckats göra större framsteg. Jag trodde jag skulle bryta ihop när jag inte förstod hur jag kunde lägga till en sträng (inte en enskild array med bara ett element) till en nuvarande array i php. Först fick jag inte till array_push men sedan så fungerade det när jag slängde om variablerna. Jag verkade ha tolkat dokumentation inkorrekt!

Något som är snurrigt än så länge är json_decode, json_encode och nödvändiga polare file_put_contents och file_get_contents. De två sistnämnda är självklara vad de gör men jag förstår inte riktigt vad som händer med textinnehållet när jag använt file_get_contents och sedan kör jag json_decode. Här kör jag då med true så det inte blir något object(StdClass)-tjofs.

När jag läser om json_encode och json_decode så pratar de båda om strängar och jag ser ingen magisk förvandling av fildata inuti JSON-filen när jag provar att var_dumpa den så jag förstår inte riktigt vad det är som har hänt med json-metoderna?

include/class/Todo.class.php

<?php // Klassnamn class Todo { // Privata variabler som bara nås via klassens metoder private $todoInput; // Inmatningsfält för Att göra private $JSONFile; // Namn på JSON-fil som ska hanteras public $todos = array(); // Alla Att göra samlade i en Array // Klassmetoder // Konstruktör - körs när ny klassinstans initierats i övrig php-kod function __construct(string $JSONFile){ // Tilldela JSON-filnamn $this->JSONFile = $JSONFile; // Kontrollera om tilldelat JSON-filnamn finns if(file_exists($JSONFile)){ // Läs in JSON-fil om den finns $file = file_get_contents($JSONFile); // Omvandla inläst JSON-fil till array och tilldela i $todos $this->todos = json_decode($file, true); } // Annars skapa JSON-fil med en tom array och tilldela $todos det else{ // Skapar filnamnet om det ej redan finns och skriver [] till filen $file = file_put_contents($JSONFile, '[]'); // Tilldelar $todos värdet av en tom array $this->todos = json_decode($file, true); } } // setTodo - tar, kontrollerar & tilldelar värde från inmatningsfält ("Sak att göra...") function setTodo(string $todoInput) : bool { // Om inmatningslängd är minst 5 tecken, dvs., fler än 4 tecken if(strlen($todoInput) > 4){ // Tilldela inmatningsvärde $this->todoInput = $todoInput; return true; // Och returnera sant, dvs., kvitto på att det gick } return false; // Annars returnera falskt, dvs., kvitto på att det INTE gick } // getTodos - returnerar JSON-arrayen med Att göra-listan function getTodos() { $file = file_get_contents($this->JSONFile); $this->todos = json_decode($file, true); return $this->todos; } // addTodo - lägger till Att göra i JSON-arrayen function addTodo(string $addtodo){ // Öppna JSON-fil $JF = file_get_contents($this->JSONFile); // Lagra den i tillfällig variabel $tempArray = json_decode($JF,true); // Lägg till sträng som array-element i den tillfälliga variabeln array_push($tempArray, $addtodo); // Spara den nya arrayen i JSON-filformat(via json_encode) igen $JFData = json_encode($tempArray); file_put_contents($this->JSONFile, $JFData); } } ?>

Dold text

p-moment3.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=6; // Initierar klass "Todo" (se även tillagd autoinkludering i config.php) och dess konstruktör med: // 1 textsträng "todolist.json", alltså filnamnet för JSON-filen ifråga $letsTodo = new Todo('todolist.json'); ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 3 - Att göra lista med PHP</h2> <div class="uppgifts-div"> <h3>En "Att göra"-lista med objektorienterad PHP</h3> <form id="form3" action="p-moment3.php" method="POST"> <label for="todoInput">Ange och lägg till ny sak att göra:</label> <input id="todoInput" type="text" name="todoItem" placeholder="Sak att göra..."> <?php // PHP-KOD som körs mellan inmatningsfält och "Lägg till"-knappen // Klickat på "Lägg till" ? if(isset($_POST['addTodo'])){ // Är det sant att minst 5 tecken har matats in? if($letsTodo->setTodo($_POST['todoItem'])){ // Lagra då "Att göra"-strängen med hjälp av klassmetod addTodo i klassen $letsTodo->addTodo($_POST['todoItem']); } // Om färre än 5 tecken, skriv ut felmeddelande else{ echo "<p id='todoErrorMsg'>Du måste ange minst fem tecken för att lägga till i listan nedan.</p>"; } } ?> <div id="todoButtons"> <input type="submit" name="addTodo" value="Lägg till" id="todoBtn"> <input type="submit" name="removeTodos" value="Rensa" id="clearBtn"> </div> </form> <div class="todoList"> <h3>Saker att göra</h3> <?php // PHP-KOD som körs nedanför inmatningsfältet och knapparna, här visas Att göra listan // Här skrivs Att göra-listan ut och klickad "Klar" via GET tas bort. // Anropa getTodos() som returnar en array från JSON-filen $Alltodos = $letsTodo->getTodos(); // Skriv ut Todo när det klickas på "Lägg till" if(isset($_POST['addTodo'])){ // För varje rad ([] är tom JSON-fil och då skrivs inget ut) så skriv ut följande foreach ($Alltodos as $todo){ echo "<div class='todo-line'>$todo <form action='p-moment3.php' method='GET'> <input type='submit' class='deleteButtons' value='Klar'></form></div>"; }} // Skriv även ut när sidan först laddas, else{ // För varje rad... osv. foreach ($Alltodos as $todo){ echo "<div class='todo-line'>$todo <form action='p-moment3.php' method='GET'> <input type='submit' class='deleteButtons' value='Klar'></form></div>"; }} ?> </div> </div> <?php include("include/c-footer.php");?>

Dold text

Nu varande skick (funktionaliteter för Rensa-knapp och Klar-knapp återstår)

"Rensa" ska tömma alla "Saker att göra" medan varje "Klar" ska ta bort den "Saker att göra"-saken på samma rad som knappen ligger på. Detta kommer genomföras genom att varje Klar-knapp får ett p-moment3.php?id=1 som då kontrolleras i klassfilen när sidan laddas och som då tar bort det elementet ur arrayen innan den "foreachas" ut.

Till sist så måste jag säga att display: flex; justify-content: space-between; är riktigt mysig!

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Glädjande framsteg med Objektorienterad PHP
Nu har jag äntligen lyckats göra större framsteg. Jag trodde jag skulle bryta ihop när jag inte förstod hur jag kunde lägga till en sträng (inte en enskild array med bara ett element) till en nuvarande array i php. Först fick jag inte till array_push men sedan så fungerade det när jag slängde om variablerna. Jag verkade ha tolkat dokumentation inkorrekt!

Något som är snurrigt än så länge är json_decode, json_encode och nödvändiga polare file_put_contents och file_get_contents. De två sistnämnda är självklara vad de gör men jag förstår inte riktigt vad som händer med textinnehållet när jag använt file_get_contents och sedan kör jag json_decode. Här kör jag då med true så det inte blir något object(StdClass)-tjofs.

När jag läser om json_encode och json_decode så pratar de båda om strängar och jag ser ingen magisk förvandling av fildata inuti JSON-filen när jag provar att var_dumpa den så jag förstår inte riktigt vad det är som har hänt med json-metoderna?

include/class/Todo.class.php

<?php // Klassnamn class Todo { // Privata variabler som bara nås via klassens metoder private $todoInput; // Inmatningsfält för Att göra private $JSONFile; // Namn på JSON-fil som ska hanteras public $todos = array(); // Alla Att göra samlade i en Array // Klassmetoder // Konstruktör - körs när ny klassinstans initierats i övrig php-kod function __construct(string $JSONFile){ // Tilldela JSON-filnamn $this->JSONFile = $JSONFile; // Kontrollera om tilldelat JSON-filnamn finns if(file_exists($JSONFile)){ // Läs in JSON-fil om den finns $file = file_get_contents($JSONFile); // Omvandla inläst JSON-fil till array och tilldela i $todos $this->todos = json_decode($file, true); } // Annars skapa JSON-fil med en tom array och tilldela $todos det else{ // Skapar filnamnet om det ej redan finns och skriver [] till filen $file = file_put_contents($JSONFile, '[]'); // Tilldelar $todos värdet av en tom array $this->todos = json_decode($file, true); } } // setTodo - tar, kontrollerar & tilldelar värde från inmatningsfält ("Sak att göra...") function setTodo(string $todoInput) : bool { // Om inmatningslängd är minst 5 tecken, dvs., fler än 4 tecken if(strlen($todoInput) > 4){ // Tilldela inmatningsvärde $this->todoInput = $todoInput; return true; // Och returnera sant, dvs., kvitto på att det gick } return false; // Annars returnera falskt, dvs., kvitto på att det INTE gick } // getTodos - returnerar JSON-arrayen med Att göra-listan function getTodos() { $file = file_get_contents($this->JSONFile); $this->todos = json_decode($file, true); return $this->todos; } // addTodo - lägger till Att göra i JSON-arrayen function addTodo(string $addtodo){ // Öppna JSON-fil $JF = file_get_contents($this->JSONFile); // Lagra den i tillfällig variabel $tempArray = json_decode($JF,true); // Lägg till sträng som array-element i den tillfälliga variabeln array_push($tempArray, $addtodo); // Spara den nya arrayen i JSON-filformat(via json_encode) igen $JFData = json_encode($tempArray); file_put_contents($this->JSONFile, $JFData); } } ?>

Dold text

p-moment3.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=6; // Initierar klass "Todo" (se även tillagd autoinkludering i config.php) och dess konstruktör med: // 1 textsträng "todolist.json", alltså filnamnet för JSON-filen ifråga $letsTodo = new Todo('todolist.json'); ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 3 - Att göra lista med PHP</h2> <div class="uppgifts-div"> <h3>En "Att göra"-lista med objektorienterad PHP</h3> <form id="form3" action="p-moment3.php" method="POST"> <label for="todoInput">Ange och lägg till ny sak att göra:</label> <input id="todoInput" type="text" name="todoItem" placeholder="Sak att göra..."> <?php // PHP-KOD som körs mellan inmatningsfält och "Lägg till"-knappen // Klickat på "Lägg till" ? if(isset($_POST['addTodo'])){ // Är det sant att minst 5 tecken har matats in? if($letsTodo->setTodo($_POST['todoItem'])){ // Lagra då "Att göra"-strängen med hjälp av klassmetod addTodo i klassen $letsTodo->addTodo($_POST['todoItem']); } // Om färre än 5 tecken, skriv ut felmeddelande else{ echo "<p id='todoErrorMsg'>Du måste ange minst fem tecken för att lägga till i listan nedan.</p>"; } } ?> <div id="todoButtons"> <input type="submit" name="addTodo" value="Lägg till" id="todoBtn"> <input type="submit" name="removeTodos" value="Rensa" id="clearBtn"> </div> </form> <div class="todoList"> <h3>Saker att göra</h3> <?php // PHP-KOD som körs nedanför inmatningsfältet och knapparna, här visas Att göra listan // Här skrivs Att göra-listan ut och klickad "Klar" via GET tas bort. // Anropa getTodos() som returnar en array från JSON-filen $Alltodos = $letsTodo->getTodos(); // Skriv ut Todo när det klickas på "Lägg till" if(isset($_POST['addTodo'])){ // För varje rad ([] är tom JSON-fil och då skrivs inget ut) så skriv ut följande foreach ($Alltodos as $todo){ echo "<div class='todo-line'>$todo <form action='p-moment3.php' method='GET'> <input type='submit' class='deleteButtons' value='Klar'></form></div>"; }} // Skriv även ut när sidan först laddas, else{ // För varje rad... osv. foreach ($Alltodos as $todo){ echo "<div class='todo-line'>$todo <form action='p-moment3.php' method='GET'> <input type='submit' class='deleteButtons' value='Klar'></form></div>"; }} ?> </div> </div> <?php include("include/c-footer.php");?>

Dold text

Nu varande skick (funktionaliteter för Rensa-knapp och Klar-knapp återstår)
<Uppladdad bildlänk>

"Rensa" ska tömma alla "Saker att göra" medan varje "Klar" ska ta bort den "Saker att göra"-saken på samma rad som knappen ligger på. Detta kommer genomföras genom att varje Klar-knapp får ett p-moment3.php?id=1 som då kontrolleras i klassfilen när sidan laddas och som då tar bort det elementet ur arrayen innan den "foreachas" ut.

Till sist så måste jag säga att display: flex; justify-content: space-between; är riktigt mysig!

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

En kommentar: Alla kommentarer, variabler, klasser, funktioner, filer ska vara skrivna på läsbar engelska! Inga undantag!

Permalänk

Jag förstår inte hur jag lyckades, men det är inlämnat redan Moment 3 i Webbutveckling II-kursen!

De två kluriga knapparna "Rensa" och "Klar" löstes med en file_put_contents för ena och en $_POST via en input type="hidden" med unika value-värden med knapp precis höger om den för andra. Det fina med file_put_contents är att den skriver över allt så att då bara föra in strängen '[]' i JSON-filen så blir det samma sak som att nollställa/tömma den.

Så länge själva utskriften av alla Saker att göra är det sista som sker i php-koden innan den skickar vidare till webbklienten så fungerar det fenomenalt. Det är en klurig grej att hålla reda på: körordningen av koden.

Klassfil

<?php // Klassnamn class Todo { // Privata variabler som bara nås via klassens metoder private $todoInput; // Inmatningsfält för Att göra private $JSONFile; // Namn på JSON-fil som ska hanteras public $todos = array(); // Alla Att göra samlade i en Array // Klassmetoder // Konstruktör - körs när ny klassinstans initierats i övrig php-kod function __construct(string $JSONFile){ // Tilldela JSON-filnamn $this->JSONFile = $JSONFile; // Kontrollera om tilldelat JSON-filnamn finns if(file_exists($JSONFile)){ // Läs in JSON-fil om den finns $file = file_get_contents($JSONFile); // Omvandla inläst JSON-fil till array och tilldela i $todos $this->todos = json_decode($file, true); } // Annars skapa JSON-fil med en tom array och tilldela $todos det else{ // Skapar filnamnet om det ej redan finns och skriver [] till filen $file = file_put_contents($JSONFile, '[]'); // Tilldelar $todos värdet av en tom array $this->todos = json_decode($file, true); } } // setTodo - tar, kontrollerar & tilldelar värde från inmatningsfält ("Sak att göra...") function setTodo(string $todoInput) : bool { // Om inmatningslängd är minst 5 tecken, dvs., fler än 4 tecken if(strlen($todoInput) > 4){ // Tilldela inmatningsvärde $this->todoInput = $todoInput; return true; // Och returnera sant, dvs., kvitto på att det gick } return false; // Annars returnera falskt, dvs., kvitto på att det INTE gick } // getTodos - returnerar JSON-arrayen med Att göra-listan function getTodos() { // Läs in JSON-fil $file = file_get_contents($this->JSONFile); // Spara JSON-fil som JSON-Array i $todos-array-variabeln $this->todos = json_decode($file, true); // Returnera $todos-arrayen return $this->todos; } // removeTodo - tar bort en vald Todo via "Klar"-knappen function removeTodo(int $arrayIndex){ // Läs in JSON-fil och omvandla till JSON-Array $file = file_get_contents($this->JSONFile); $jsonArray = json_decode($file, true); // Ta bort vald aktivitet ur arrayen tack vare dess mottagna indexvärde unset($jsonArray[$arrayIndex]); // Spara tillbaka som en JSON-fil igen file_put_contents($this->JSONFile, json_encode($jsonArray, JSON_PRETTY_PRINT)); } // clearTodos - rensar hela JSON-arrayen function clearTodos(){ // Rensar JSON-filen genom att bara skriva in tom array på nytt i den vilket då överskriver allt annat file_put_contents($this->JSONFile,'[]'); } // addTodo - lägger till Att göra i JSON-arrayen function addTodo(string $addtodo){ // Öppna JSON-fil $JF = file_get_contents($this->JSONFile); // Lagra den i tillfällig variabel $tempArray = json_decode($JF,true); // Lägg till sträng som array-element i den tillfälliga variabeln array_push($tempArray, $addtodo); // Spara den nya arrayen i JSON-filformat(via json_encode) igen $JFData = json_encode($tempArray); file_put_contents($this->JSONFile, $JFData); } } ?>

Dold text

Momentfil

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=6; // Initierar klass "Todo" (se även tillagd autoinkludering i config.php) och dess konstruktör med: // 1 textsträng "todolist.json", alltså filnamnet för JSON-filen ifråga $letsTodo = new Todo('todolist.json'); ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 3 - Att göra lista med PHP</h2> <div class="uppgifts-div"> <h3>En "Att göra"-lista med objektorienterad PHP</h3> <form id="form3" action="p-moment3.php" method="POST"> <label for="todoInput">Ange och lägg till ny sak att göra:</label> <input id="todoInput" type="text" name="todoItem" placeholder="Sak att göra..."> <?php // PHP-KOD som körs mellan inmatningsfält och "Lägg till"-knappen // Klickat på "Lägg till" ? if(isset($_POST['addTodo'])){ // Är det sant att minst 5 tecken har matats in? if($letsTodo->setTodo($_POST['todoItem'])){ // Lagra då "Att göra"-strängen med hjälp av klassmetod addTodo i klassen $letsTodo->addTodo($_POST['todoItem']); } // Om färre än 5 tecken, skriv ut felmeddelande else{ echo "<p id='todoErrorMsg'>Du måste ange minst fem tecken för att lägga till i listan nedan.</p>"; } } ?> <div id="todoButtons"> <input type="submit" name="addTodo" value="Lägg till" id="todoBtn"> <input type="submit" name="clearTodos" value="Rensa" id="clearBtn"> </div> </form> <div class="todoList"> <h3>Saker att göra</h3> <?php // PHP-KOD som körs nedanför inmatningsfältet och knapparna, här visas "Saker att göra"-listan $key_plus_one = 0; // Används för att numrera aktiviteterna innan de skrivs ut // Kontrollera om "Rensa" klickats på och anropa då klassmetod som tömmer hela arrayen innan den skrivs ut if(isset($_POST['clearTodos'])){ $letsTodo->clearTodos(); $key_plus_one = 0; // Allt togs bort så nollställ index } // Kontrollera om någon "Klar" klickats på if(isset($_POST['delete'])){ // Skicka med arrayIndex (omvandlat till heltal) för // vald aktivitet & ta bort med removeTodo()-klassmetoden $letsTodo->removeTodo(intval($_POST['delete'])); $key_plus_one = 0; // Något togs bort så nollställ index } // Här skrivs Att göra-listan ut och klickad "Klar" via GET tas bort. // Anropa getTodos() som returnar en array från JSON-filen // DETTA ÄR DET SISTA SOM SKA SKE PÅ SIDAN EFTERSOM DEN SKRIVER UT DET SLUTGILTIGA SOM FINNS! $Alltodos = $letsTodo->getTodos(); // Skriv ut alla "Saker att göra" från erhållna arrayen foreach ($Alltodos as $key=>$todo){ $key_plus_one += 1; // Med hjälp av $key så kan varje Klar-knapp tilldelas unikt ID när 'POST'-metoden används // som då också är säkrare än 'GET'. echo "<div class='todo-line'>$key_plus_one. $todo <form action='p-moment3.php' method='POST'> <input type='hidden' value=$key name=delete><button class='deleteButtons'>Klar</button></form></div>"; } ?> </div> </div> <?php include("include/c-footer.php");?>

Dold text

Klass-filen är knappt 100 rader kod (inklusive massa mellanrum för luftighet), medan Moment-filen ligger på cirka 80 rader inklusive massa luftiga mellanrum.

Den som vill kika på främst Klassfilen där all magi inträffar får gärna kommentera om just detta är "rätt objektorienterad php-kod" eller om det är något jag har missat i hur man ska strukturera för att följa branschstandarder inom objektorienterad php. Jag har nämligen ingen aning om jag har följt rätt praxis. Men buggfritt fungerar det i alla fall!

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Skrivet av medbor:

En kommentar: Alla kommentarer, variabler, klasser, funktioner, filer ska vara skrivna på läsbar engelska! Inga undantag!

I know, my bad!

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Databaser Labb 2 äntligen inlämnad!

Jag är äntligen färdig med Databas-kursens Labb 2 som handlade om skapandet av SQL-tabeller, infogandet av data i dessa tabeller såväl som SQL-frågor från dessa för att knyta ihop säcken.

Nedanför finns alla SQL-filer för den som själv vill provköra det i en DBMS med SQL-stöd:

Basrelationer

-- Basrelation från entitet "Butik" på engelska STORE(Store_number(PK),Address_street,Address_city,Address_housenumber,Address_zipcode,Telephone_number,Store_boss_name,Store_name) ---------------------------------------------------------- -- Basrelation från entitet "Anställd" på engelska EMPLOYEE(Employment_number(PK),Employed_at(FK),Firstname,Lastname,Salary) -- Basrelationskopplingar -- Employed_at(FK) = Butik ---------------------------------------------------------- -- Basrelation från entitet "Medlem" på engelska MEMBER(Member_number(PK),Store_number(FK),Registered_date,Member_firstname,Member_lastname,Member_address_street,Member_address_city,Member_address_housenumber,Member_address_zipcode) -- Basrelationskopplingar -- Store_number(FK) = Butik ---------------------------------------------------------- -- Basrelation från entitet "Film" på engelska MOVIE(Movie_number(PK),Movie_name,Movie_length,Movie_main_category) ---------------------------------------------------------- -- Basrelation från sambandstyp "Hyr" på engelska RENTING((Movie_number+Member_number+Renting_number(PK)),Movie(FK),Member_number(FK),Renting_number,Renting_start_date,Renting_return_date) -- Basrelationskopplingar -- Movie(FK) = Film -- Member_number(FK) = Medlem ---------------------------------------------------------- -- Basrelation från sambandstyp "som_lagrar" på engelska STORING((Movie_number+Store_number+Shelf_number(PK)),Store_number(FK),Movie(FK),Shelf_number,Copies_per_movie,Renting_price) -- Basrelationskopplingar -- Store_number(FK) = Butik -- Movie_number(FK) = Film ---------------------------------------------------------- -- Basrelation från sambandstyp "har_en_arbetsroll_på" på engelska WORK_ROLES((Employment_number+Store_number(PK)),Employment_number(FK),Employed_at(FK),Working_role) -- Basrelationskopplingar -- Employment_number(FK) = Anställd -- Employed_at(FK) = Butik ---------------------------------------------------------- -- Basrelation från flervärdesattribut "Regissör" på engelska DIRECTOR(Movie(FK),First_name,Last_name) -- Basrelationskopplingar -- Movie_number(FK) = Film ---------------------------------------------------------- -- Basrelation från flervärdesattribut "Skådespelare" på engelska ACTOR(Movie(FK),First_name,Last_name,Movie_role) -- Basrelationskopplingar -- Movie_number(FK) = Film

Dold text

Labb2_CREATES.sql

CREATE DATABASE IF NOT EXISTS Labb2; USE Labb2; -- CREATING TABLES -- Tabell "Butik" CREATE TABLE STORE ( Store_number INT NOT NULL, -- Make Primary Key in Constraints Address_city VARCHAR(64) NOT NULL, Address_street VARCHAR(64) NOT NULL, Address_housenumber INT(6) NOT NULL, Address_zipcode INT(5) NOT NULL, Telephone_number INT(12) NOT NULL, Store_boss_name VARCHAR(64) NOT NULL, Store_name VARCHAR(64) NOT NULL); -- Tabell "Anställd" CREATE TABLE EMPLOYEE ( Employment_number INT NOT NULL, -- Make Primary Key in Constraints Firstname VARCHAR(64) NOT NULL, Lastname VARCHAR(64) NOT NULL, Salary DECIMAL(8,2) NOT NULL, Employed_at INT NOT NULL); -- Foreign key to Table "WORK_ROLES" -- Tabell "Medlem" CREATE TABLE MEMBER ( Member_number INT NOT NULL, -- Make Primary Key in Constraints Registered_date DATE NOT NULL, Member_firstname VARCHAR(64) NOT NULL, Member_lastname VARCHAR(64) NOT NULL, Member_address_street VARCHAR(64) NOT NULL, Member_address_city VARCHAR(64) NOT NULL, Member_address_housenumber VARCHAR(64) NOT NULL, Member_address_zipcode VARCHAR(64) NOT NULL, Store_number INT NOT NULL); -- Foreign key to Table "STORE" -- Tabell "Film" CREATE TABLE MOVIE ( Movie_number INT NOT NULL, -- Make Primary Key in Constraints Movie_name VARCHAR(64) NOT NULL, Movie_length INT(3) NOT NULL, Movie_main_category VARCHAR(64) NOT NULL); -- Tabell "Hyr" CREATE TABLE RENTING ( Movie_number INT NOT NULL, -- Foreign key to Table "MOVIE" Member_number INT NOT NULL, -- Foreign key to Table "MEMBER" Renting_number INT NOT NULL, -- Make Composite Key ?? Renting_start_date DATE NOT NULL, Renting_return_date DATE NOT NULL); -- Tabell "som_lagrar" CREATE TABLE STORING ( Movie_number INT NOT NULL, -- Foreign key to Table "MOVIE" Store_number INT NOT NULL, -- Foreign key to Table "STORE" Shelf_number INT NOT NULL, -- Make Composite Key ?? Copies_per_movie INT NOT NULL, Renting_price INT NOT NULL); -- Tabell "har_en_arbetsroll_på" CREATE TABLE WORK_ROLES ( Employment_number INT NOT NULL, -- Make Composite Key ?? Store_number INT NOT NULL, -- Make Composite Key ?? Working_role VARCHAR(64) NOT NULL); -- Tabell "Regissör" CREATE TABLE DIRECTOR ( First_name VARCHAR(64) NOT NULL, Last_name VARCHAR(64) NOT NULL, Movie_number INT NOT NULL); -- Foreign key to Table "MOVIE" -- Tabell "Skådespelare" CREATE TABLE ACTOR ( First_name VARCHAR(64) NOT NULL, Last_name VARCHAR(64) NOT NULL, Movie_number INT NOT NULL, -- Foreign key to Table "MOVIE" Movie_role VARCHAR(64) NOT NULL);

Dold text

Labb2_CONSTRAINTS.sql

-- SET PRIMARY KEYS FOR TABLES -- Tabell "Butik" ALTER TABLE STORE ADD CONSTRAINT Store_pk PRIMARY KEY (Store_number); ALTER TABLE STORE ADD UNIQUE (Store_number); -- Tabell "Anställd" ALTER TABLE EMPLOYEE ADD CONSTRAINT Employee_pk PRIMARY KEY (Employment_number); -- Tabell "Medlem" ALTER TABLE MEMBER ADD CONSTRAINT Member_pk PRIMARY KEY (Member_number); -- Tabell "Film" ALTER TABLE MOVIE ADD CONSTRAINT Movie_pk PRIMARY KEY (Movie_number); -- Tabell "har_en_arbetsroll_på" ALTER TABLE WORK_ROLES ADD CONSTRAINT WorkRoles_pk PRIMARY KEY (Employment_number, Store_number); ALTER TABLE WORK_ROLES ADD UNIQUE (Employment_number); -- Tabell "som_lagrar" ALTER TABLE STORING ADD CONSTRAINT Storing_pk PRIMARY KEY (Movie_number, Store_number, Shelf_number); -- Tabell "Film" ALTER TABLE RENTING ADD CONSTRAINT Renting_pk PRIMARY KEY (Movie_number, Member_number, Renting_number); ALTER TABLE RENTING ADD UNIQUE (Renting_number); -- SET FOREIGN KEYS FOR TABLES -- Tabell "Anställd" ALTER TABLE EMPLOYEE ADD CONSTRAINT Employee_Store_fk FOREIGN KEY (Employed_at) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "Medlem" ALTER TABLE MEMBER ADD CONSTRAINT Member_at_fk FOREIGN KEY (Store_number) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "har_en_arbetsroll_på" ALTER TABLE WORK_ROLES ADD CONSTRAINT WR_Emp_fk FOREIGN KEY (Employment_number) REFERENCES EMPLOYEE(Employment_number) ON UPDATE CASCADE; ALTER TABLE WORK_ROLES ADD CONSTRAINT WR_Store_fk FOREIGN KEY (Store_number) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "Hyr" ALTER TABLE RENTING ADD CONSTRAINT RentMember_at_fk FOREIGN KEY (Member_number) REFERENCES MEMBER(Member_number) ON UPDATE CASCADE; ALTER TABLE RENTING ADD CONSTRAINT RentMovie_at_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE; -- Tabell "som_lagrar" ALTER TABLE STORING ADD CONSTRAINT StoringMovie_at_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE; ALTER TABLE STORING ADD CONSTRAINT StoringStore_at_fk FOREIGN KEY (Store_number) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "Regissör" ALTER TABLE DIRECTOR ADD CONSTRAINT Director_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE; -- Tabell "Skådespelare" ALTER TABLE ACTOR ADD CONSTRAINT Actor_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE;

Dold text

Labb2_QUERIES.sql

-- SQL-Fråga:"Visa alla som INTE heter -- Stekmark i efternamn från tabellen MEMBER" SELECT * FROM MEMBER where Member_lastname!='Stekmark'; -- SQL-Fråga:"Visa alla från tabellen MEMBER där förnamnet -- innehåller ett stort eller litet F i början av namnet" SELECT * FROM MEMBER where Member_firstname LIKE 'F%'; -- SQL-Fråga:"Visa alla som är registrerade i Januari 2023" SELECT Registered_date FROM MEMBER WHERE Registered_date BETWEEN '2023-01-01' AND '2023-01-31'; -- SQL-Fråga:"Visa vem som har lägst lön i -- varje Butik, gruppera efter Butiks-nr" SELECT min(Salary) FROM EMPLOYEE GROUP BY Employed_at; -- SQL-Fråga:"Gruppera filmneras körtid från lägst -- till högst så länge de är över 120 minuter långa" SELECT * FROM MOVIE GROUP BY Movie_length HAVING Movie_length > 120; -- SQL-Fråga:"Gruppera filmneras körtid från lägst -- till högst så länge de är en Kriminalfilm" SELECT * FROM MOVIE GROUP BY Movie_length HAVING Movie_main_category="Kriminalfilm"; -- SQL-Fråga:"Visa alla från tabellen MEMBER -- i bokstavsordning A till Ö för förnamn" SELECT * FROM MEMBER ORDER BY Member_firstname asc; -- SQL-Fråga:"Ändra lönen till 99999 kr för anställd nr 12" UPDATE EMPLOYEE SET SALARY=99999 WHERE Employment_number=12; -- SQL-Fåga:"Visa vilka anställda -- som har över 20 000 kronor i lön" SELECT * FROM EMPLOYEE WHERE Employment_number IN (SELECT Employment_number FROM EMPLOYEE WHERE Salary > 20000);

Dold text

Labb2_INSERTS.sql

SET foreign_key_checks = 0; -- Fiktiva värden Tabell "STORE" INSERT INTO STORE VALUES (1,'Stekstaden','Schysstvägen','69','86035','0694201340','Jörgen Jönsson','Hyra Film AB'), (2,'Stekstaden','Drakvägen','420','86035','0694201338','Jörgen Jönsson','Hyra Film AB'), (3,'Stekstaden','Mustvägen','1337','86035','0694201337','Jörgen Jönsson','Hyra Film AB'), (4,'Stekstaden','Fiolvägen','1','86035','0694201339','Jörgen Jönsson','Hyra Film AB'); -- Fiktiva värden Tabell "EMPLOYEE" INSERT INTO EMPLOYEE VALUES (1,'Stefan','Olsson',13200,1), (2,'Ulla','Jonsson',11369,1), (3,'Alban','Sheesh',15420,1), (4,'Inger','Yolo',13469,1), (5,'Johan','Stenmark',13350,2), (6,'Pelle','Borg',13309,2), (7,'Elin','Salming',13400,2), (8,'Inga','Rövhalt',16461,2), (9,'Viola','Rosenspira',14332,3), (10,'Magnus','Meckberg',15665,3), (11,'Kristoffer','Själqvist',13651,3), (12,'Pontus','Pilatus',16464,3), (13,'Krister','Hansson',11210,4), (14,'Inga-Britt','Ztek',17569,4), (15,'Annicka','Markbock',19999,4), (16,'Markus','Häänssåån',12222,4); -- Fiktiva värden Tabell "MEMBER" INSERT INTO MEMBER VALUES (1,'2023-01-01','Monika','Stekmark','Stekarvägen','Stekstaden','420','86039',1), (2,'2023-01-08','Alban','Albansson','Kråkvägen','Stekstaden','10','86039',1), (3,'2023-01-09','Mulle','Meck','Chokladvägen','Stekstaden','33','86039',2), (4,'2023-01-10','Agnes','Figenstia','Potatisvägen','Stekstaden','40','86039',2), (5,'2023-01-15','Urban','Rundqvist','Fläskvägen','Stekstaden','20','86039',3), (6,'2023-01-20','Fabian','Sibkvast','Stekvägen','Stekstaden','4','86039',3), (7,'2023-01-25','Jannicka','Almqvist','Najsvägen','Stekstaden','69','86039',4), (8,'2023-02-05','Thomas','Quist','Majsvägen','Stekstaden','1337','86039',4); -- Fiktiva värden Tabell "MOVIE" INSERT INTO MOVIE VALUES (1,'The Shawshank Redemption',142,'Drama'), (2,'The Godfather',175,'Kriminalfilm'), (3,'The Dark Knight',152,'Action'), (4,'The Godfather Part II',202,'Kriminalfilm'), (5,'12 Angry Men',96,'Kriminalfilm'), (6,'Schindler''s List',142,'Drama'), (7,'The Lord of the Rings: The Return of the King',201,'Actionfilm'), (8,'Pulp Fiction',154,'Kriminalfilm'), (9,'The Lord of the Rings: The Fellowship of the Ring',178,'Drama'), (10,'The Good, the Bad and the Ugly',178,'Västernfilm'); -- Fiktiva värden Tabell "DIRECTOR" INSERT INTO DIRECTOR VALUES ('Frank','Darabont',1), ('Francis Ford','Coppola',2), ('Christopher','Nolan',3), ('Francis Ford','Coppola',4), ('Sidney','Lumet',5), ('Steven','Spielberg',6), ('Peter','Jackson',7), ('Quentin','Tarantino',8), ('Peter','Jackson',9), ('Sergio','Leone',10); -- Fiktiva värden Tabell "ACTOR" INSERT INTO ACTOR VALUES ('Tim','Robbin',1,'Andy Dufresne'), ('Marlon','Brando',2,'Don Vito Corleone'), ('Christian','Bale',3,'Bruce Wayne / Batman'), ('Al','Pacino',4,'Michael'), ('Henry','Fonda',5,'Juror 8'), ('Liam','Neeson',6,'Oskar Schindler'), ('Elijah','Wood',7,'Frodo'), ('John','Travolta',8,'Vincent Vega'), ('Elijah','Wood',9,'Frodo'), ('Clint','Eastwood',10,'Blondie'); -- Fiktiva värden Tabell "WORK_ROLES" INSERT INTO WORK_ROLES VALUES (1,1,'Lagerarbetare'), (2,1,'Kassör'), (3,1,'Städtekniker'), (4,1,'Lagerarbetare'), (5,2,'Lagerarbetare'), (6,2,'Kassör'), (7,2,'Städtekniker'), (8,2,'Lagerarbetare'), (9,3,'Lagerarbetare'), (10,3,'Kassör'), (11,3,'Städtekniker'), (12,3,'Lagerarbetare'), (13,4,'Lagerarbetare'), (14,4,'Kassör'), (15,4,'Städtekniker'), (16,4,'Lagerarbetare'); -- Fiktiva värden Tabell "STORING",(FilmNr,ButikNr,HyllNr,Kopior,Pris) INSERT INTO STORING VALUES (1,1,1,5,50), (2,1,1,5,70), (3,1,1,5,60), (4,1,1,5,55), (5,1,1,5,55), (6,1,1,5,45), (7,1,1,5,45), (8,1,1,5,40), (9,1,1,5,60), (10,1,1,5,50), (1,2,1,5,55), (2,2,1,5,70), (3,2,1,5,65), (4,2,1,5,40), (5,2,1,5,45), (6,2,1,5,40), (7,2,1,5,45), (8,2,1,5,50), (9,2,1,5,65), (10,2,1,5,60), (1,3,1,5,55), (2,3,1,5,55), (3,3,1,5,40), (4,3,1,5,40), (5,3,1,5,45), (6,3,1,5,40), (7,3,1,5,75), (8,3,1,5,70), (9,3,1,5,65), (10,3,1,5,60), (1,4,1,5,45), (2,4,1,5,40), (3,4,1,5,70), (4,4,1,5,65), (5,4,1,5,45), (6,4,1,5,55), (7,4,1,5,45), (8,4,1,5,40), (9,4,1,5,60), (10,4,1,5,55); -- Fiktiva värden Tabell "RENTING",(FilmNr,MedlemNr,HyrNr,HyrStartDatum,HyrReturDatum) INSERT INTO RENTING VALUES (1,1,1,'2023-02-14','2023-02-21'), (2,1,2,'2023-02-14','2023-02-21'), (3,1,3,'2023-02-14','2023-02-21'), (4,1,4,'2023-02-14','2023-02-21'), (5,1,5,'2023-02-14','2023-02-21'), (6,1,6,'2023-02-14','2023-02-21'), (7,1,7,'2023-02-14','2023-02-21'), (8,1,8,'2023-02-14','2023-02-21'), (9,1,9,'2023-02-14','2023-02-21'), (10,1,10,'2023-02-14','2023-02-21'), (1,1,11,'2023-02-14','2023-02-22'), (2,1,12,'2023-02-14','2023-02-23'), (3,1,13,'2023-02-14','2023-02-24'), (4,1,14,'2023-02-14','2023-02-25'), (5,1,15,'2023-02-14','2023-02-26'), (6,1,16,'2023-02-14','2023-02-27'), (7,1,17,'2023-02-14','2023-02-28'), (8,1,18,'2023-02-14','2023-02-28'), (9,1,19,'2023-02-14','2023-02-28'), (10,1,20,'2023-02-14','2023-02-28'); SET foreign_key_checks = 1;

Dold text

Imorgon inleds redan Labb 3 i Databas-kursen vilket kommer att vara Normalisering och Normalformer!

Moment 4 i Webbutveckling II är att skapa en mycket simpel nyhets- eller bloggsida med registrering och inloggning för en person i taget som ska kunna posta och visa de senaste inlagda inläggen. Projektuppgiften är en större och mer omfattande variant av denna.

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Jag är äntligen färdig med Databas-kursens Labb 2 som handlade om skapandet av SQL-tabeller, infogandet av data i dessa tabeller såväl som SQL-frågor från dessa för att knyta ihop säcken.

Nedanför finns alla SQL-filer för den som själv vill provköra det i en DBMS med SQL-stöd:

Basrelationer

-- Basrelation från entitet "Butik" på engelska STORE(Store_number(PK),Address_street,Address_city,Address_housenumber,Address_zipcode,Telephone_number,Store_boss_name,Store_name) ---------------------------------------------------------- -- Basrelation från entitet "Anställd" på engelska EMPLOYEE(Employment_number(PK),Employed_at(FK),Firstname,Lastname,Salary) -- Basrelationskopplingar -- Employed_at(FK) = Butik ---------------------------------------------------------- -- Basrelation från entitet "Medlem" på engelska MEMBER(Member_number(PK),Store_number(FK),Registered_date,Member_firstname,Member_lastname,Member_address_street,Member_address_city,Member_address_housenumber,Member_address_zipcode) -- Basrelationskopplingar -- Store_number(FK) = Butik ---------------------------------------------------------- -- Basrelation från entitet "Film" på engelska MOVIE(Movie_number(PK),Movie_name,Movie_length,Movie_main_category) ---------------------------------------------------------- -- Basrelation från sambandstyp "Hyr" på engelska RENTING((Movie_number+Member_number+Renting_number(PK)),Movie(FK),Member_number(FK),Renting_number,Renting_start_date,Renting_return_date) -- Basrelationskopplingar -- Movie(FK) = Film -- Member_number(FK) = Medlem ---------------------------------------------------------- -- Basrelation från sambandstyp "som_lagrar" på engelska STORING((Movie_number+Store_number+Shelf_number(PK)),Store_number(FK),Movie(FK),Shelf_number,Copies_per_movie,Renting_price) -- Basrelationskopplingar -- Store_number(FK) = Butik -- Movie_number(FK) = Film ---------------------------------------------------------- -- Basrelation från sambandstyp "har_en_arbetsroll_på" på engelska WORK_ROLES((Employment_number+Store_number(PK)),Employment_number(FK),Employed_at(FK),Working_role) -- Basrelationskopplingar -- Employment_number(FK) = Anställd -- Employed_at(FK) = Butik ---------------------------------------------------------- -- Basrelation från flervärdesattribut "Regissör" på engelska DIRECTOR(Movie(FK),First_name,Last_name) -- Basrelationskopplingar -- Movie_number(FK) = Film ---------------------------------------------------------- -- Basrelation från flervärdesattribut "Skådespelare" på engelska ACTOR(Movie(FK),First_name,Last_name,Movie_role) -- Basrelationskopplingar -- Movie_number(FK) = Film

Dold text

Labb2_CREATES.sql

CREATE DATABASE IF NOT EXISTS Labb2; USE Labb2; -- CREATING TABLES -- Tabell "Butik" CREATE TABLE STORE ( Store_number INT NOT NULL, -- Make Primary Key in Constraints Address_city VARCHAR(64) NOT NULL, Address_street VARCHAR(64) NOT NULL, Address_housenumber INT(6) NOT NULL, Address_zipcode INT(5) NOT NULL, Telephone_number INT(12) NOT NULL, Store_boss_name VARCHAR(64) NOT NULL, Store_name VARCHAR(64) NOT NULL); -- Tabell "Anställd" CREATE TABLE EMPLOYEE ( Employment_number INT NOT NULL, -- Make Primary Key in Constraints Firstname VARCHAR(64) NOT NULL, Lastname VARCHAR(64) NOT NULL, Salary DECIMAL(8,2) NOT NULL, Employed_at INT NOT NULL); -- Foreign key to Table "WORK_ROLES" -- Tabell "Medlem" CREATE TABLE MEMBER ( Member_number INT NOT NULL, -- Make Primary Key in Constraints Registered_date DATE NOT NULL, Member_firstname VARCHAR(64) NOT NULL, Member_lastname VARCHAR(64) NOT NULL, Member_address_street VARCHAR(64) NOT NULL, Member_address_city VARCHAR(64) NOT NULL, Member_address_housenumber VARCHAR(64) NOT NULL, Member_address_zipcode VARCHAR(64) NOT NULL, Store_number INT NOT NULL); -- Foreign key to Table "STORE" -- Tabell "Film" CREATE TABLE MOVIE ( Movie_number INT NOT NULL, -- Make Primary Key in Constraints Movie_name VARCHAR(64) NOT NULL, Movie_length INT(3) NOT NULL, Movie_main_category VARCHAR(64) NOT NULL); -- Tabell "Hyr" CREATE TABLE RENTING ( Movie_number INT NOT NULL, -- Foreign key to Table "MOVIE" Member_number INT NOT NULL, -- Foreign key to Table "MEMBER" Renting_number INT NOT NULL, -- Make Composite Key ?? Renting_start_date DATE NOT NULL, Renting_return_date DATE NOT NULL); -- Tabell "som_lagrar" CREATE TABLE STORING ( Movie_number INT NOT NULL, -- Foreign key to Table "MOVIE" Store_number INT NOT NULL, -- Foreign key to Table "STORE" Shelf_number INT NOT NULL, -- Make Composite Key ?? Copies_per_movie INT NOT NULL, Renting_price INT NOT NULL); -- Tabell "har_en_arbetsroll_på" CREATE TABLE WORK_ROLES ( Employment_number INT NOT NULL, -- Make Composite Key ?? Store_number INT NOT NULL, -- Make Composite Key ?? Working_role VARCHAR(64) NOT NULL); -- Tabell "Regissör" CREATE TABLE DIRECTOR ( First_name VARCHAR(64) NOT NULL, Last_name VARCHAR(64) NOT NULL, Movie_number INT NOT NULL); -- Foreign key to Table "MOVIE" -- Tabell "Skådespelare" CREATE TABLE ACTOR ( First_name VARCHAR(64) NOT NULL, Last_name VARCHAR(64) NOT NULL, Movie_number INT NOT NULL, -- Foreign key to Table "MOVIE" Movie_role VARCHAR(64) NOT NULL);

Dold text

Labb2_CONSTRAINTS.sql

-- SET PRIMARY KEYS FOR TABLES -- Tabell "Butik" ALTER TABLE STORE ADD CONSTRAINT Store_pk PRIMARY KEY (Store_number); ALTER TABLE STORE ADD UNIQUE (Store_number); -- Tabell "Anställd" ALTER TABLE EMPLOYEE ADD CONSTRAINT Employee_pk PRIMARY KEY (Employment_number); -- Tabell "Medlem" ALTER TABLE MEMBER ADD CONSTRAINT Member_pk PRIMARY KEY (Member_number); -- Tabell "Film" ALTER TABLE MOVIE ADD CONSTRAINT Movie_pk PRIMARY KEY (Movie_number); -- Tabell "har_en_arbetsroll_på" ALTER TABLE WORK_ROLES ADD CONSTRAINT WorkRoles_pk PRIMARY KEY (Employment_number, Store_number); ALTER TABLE WORK_ROLES ADD UNIQUE (Employment_number); -- Tabell "som_lagrar" ALTER TABLE STORING ADD CONSTRAINT Storing_pk PRIMARY KEY (Movie_number, Store_number, Shelf_number); -- Tabell "Film" ALTER TABLE RENTING ADD CONSTRAINT Renting_pk PRIMARY KEY (Movie_number, Member_number, Renting_number); ALTER TABLE RENTING ADD UNIQUE (Renting_number); -- SET FOREIGN KEYS FOR TABLES -- Tabell "Anställd" ALTER TABLE EMPLOYEE ADD CONSTRAINT Employee_Store_fk FOREIGN KEY (Employed_at) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "Medlem" ALTER TABLE MEMBER ADD CONSTRAINT Member_at_fk FOREIGN KEY (Store_number) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "har_en_arbetsroll_på" ALTER TABLE WORK_ROLES ADD CONSTRAINT WR_Emp_fk FOREIGN KEY (Employment_number) REFERENCES EMPLOYEE(Employment_number) ON UPDATE CASCADE; ALTER TABLE WORK_ROLES ADD CONSTRAINT WR_Store_fk FOREIGN KEY (Store_number) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "Hyr" ALTER TABLE RENTING ADD CONSTRAINT RentMember_at_fk FOREIGN KEY (Member_number) REFERENCES MEMBER(Member_number) ON UPDATE CASCADE; ALTER TABLE RENTING ADD CONSTRAINT RentMovie_at_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE; -- Tabell "som_lagrar" ALTER TABLE STORING ADD CONSTRAINT StoringMovie_at_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE; ALTER TABLE STORING ADD CONSTRAINT StoringStore_at_fk FOREIGN KEY (Store_number) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "Regissör" ALTER TABLE DIRECTOR ADD CONSTRAINT Director_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE; -- Tabell "Skådespelare" ALTER TABLE ACTOR ADD CONSTRAINT Actor_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE;

Dold text

Labb2_QUERIES.sql

-- SQL-Fråga:"Visa alla som INTE heter -- Stekmark i efternamn från tabellen MEMBER" SELECT * FROM MEMBER where Member_lastname!='Stekmark'; -- SQL-Fråga:"Visa alla från tabellen MEMBER där förnamnet -- innehåller ett stort eller litet F i början av namnet" SELECT * FROM MEMBER where Member_firstname LIKE 'F%'; -- SQL-Fråga:"Visa alla som är registrerade i Januari 2023" SELECT Registered_date FROM MEMBER WHERE Registered_date BETWEEN '2023-01-01' AND '2023-01-31'; -- SQL-Fråga:"Visa vem som har lägst lön i -- varje Butik, gruppera efter Butiks-nr" SELECT min(Salary) FROM EMPLOYEE GROUP BY Employed_at; -- SQL-Fråga:"Gruppera filmneras körtid från lägst -- till högst så länge de är över 120 minuter långa" SELECT * FROM MOVIE GROUP BY Movie_length HAVING Movie_length > 120; -- SQL-Fråga:"Gruppera filmneras körtid från lägst -- till högst så länge de är en Kriminalfilm" SELECT * FROM MOVIE GROUP BY Movie_length HAVING Movie_main_category="Kriminalfilm"; -- SQL-Fråga:"Visa alla från tabellen MEMBER -- i bokstavsordning A till Ö för förnamn" SELECT * FROM MEMBER ORDER BY Member_firstname asc; -- SQL-Fråga:"Ändra lönen till 99999 kr för anställd nr 12" UPDATE EMPLOYEE SET SALARY=99999 WHERE Employment_number=12; -- SQL-Fåga:"Visa vilka anställda -- som har över 20 000 kronor i lön" SELECT * FROM EMPLOYEE WHERE Employment_number IN (SELECT Employment_number FROM EMPLOYEE WHERE Salary > 20000);

Dold text

Labb2_INSERTS.sql

SET foreign_key_checks = 0; -- Fiktiva värden Tabell "STORE" INSERT INTO STORE VALUES (1,'Stekstaden','Schysstvägen','69','86035','0694201340','Jörgen Jönsson','Hyra Film AB'), (2,'Stekstaden','Drakvägen','420','86035','0694201338','Jörgen Jönsson','Hyra Film AB'), (3,'Stekstaden','Mustvägen','1337','86035','0694201337','Jörgen Jönsson','Hyra Film AB'), (4,'Stekstaden','Fiolvägen','1','86035','0694201339','Jörgen Jönsson','Hyra Film AB'); -- Fiktiva värden Tabell "EMPLOYEE" INSERT INTO EMPLOYEE VALUES (1,'Stefan','Olsson',13200,1), (2,'Ulla','Jonsson',11369,1), (3,'Alban','Sheesh',15420,1), (4,'Inger','Yolo',13469,1), (5,'Johan','Stenmark',13350,2), (6,'Pelle','Borg',13309,2), (7,'Elin','Salming',13400,2), (8,'Inga','Rövhalt',16461,2), (9,'Viola','Rosenspira',14332,3), (10,'Magnus','Meckberg',15665,3), (11,'Kristoffer','Själqvist',13651,3), (12,'Pontus','Pilatus',16464,3), (13,'Krister','Hansson',11210,4), (14,'Inga-Britt','Ztek',17569,4), (15,'Annicka','Markbock',19999,4), (16,'Markus','Häänssåån',12222,4); -- Fiktiva värden Tabell "MEMBER" INSERT INTO MEMBER VALUES (1,'2023-01-01','Monika','Stekmark','Stekarvägen','Stekstaden','420','86039',1), (2,'2023-01-08','Alban','Albansson','Kråkvägen','Stekstaden','10','86039',1), (3,'2023-01-09','Mulle','Meck','Chokladvägen','Stekstaden','33','86039',2), (4,'2023-01-10','Agnes','Figenstia','Potatisvägen','Stekstaden','40','86039',2), (5,'2023-01-15','Urban','Rundqvist','Fläskvägen','Stekstaden','20','86039',3), (6,'2023-01-20','Fabian','Sibkvast','Stekvägen','Stekstaden','4','86039',3), (7,'2023-01-25','Jannicka','Almqvist','Najsvägen','Stekstaden','69','86039',4), (8,'2023-02-05','Thomas','Quist','Majsvägen','Stekstaden','1337','86039',4); -- Fiktiva värden Tabell "MOVIE" INSERT INTO MOVIE VALUES (1,'The Shawshank Redemption',142,'Drama'), (2,'The Godfather',175,'Kriminalfilm'), (3,'The Dark Knight',152,'Action'), (4,'The Godfather Part II',202,'Kriminalfilm'), (5,'12 Angry Men',96,'Kriminalfilm'), (6,'Schindler''s List',142,'Drama'), (7,'The Lord of the Rings: The Return of the King',201,'Actionfilm'), (8,'Pulp Fiction',154,'Kriminalfilm'), (9,'The Lord of the Rings: The Fellowship of the Ring',178,'Drama'), (10,'The Good, the Bad and the Ugly',178,'Västernfilm'); -- Fiktiva värden Tabell "DIRECTOR" INSERT INTO DIRECTOR VALUES ('Frank','Darabont',1), ('Francis Ford','Coppola',2), ('Christopher','Nolan',3), ('Francis Ford','Coppola',4), ('Sidney','Lumet',5), ('Steven','Spielberg',6), ('Peter','Jackson',7), ('Quentin','Tarantino',8), ('Peter','Jackson',9), ('Sergio','Leone',10); -- Fiktiva värden Tabell "ACTOR" INSERT INTO ACTOR VALUES ('Tim','Robbin',1,'Andy Dufresne'), ('Marlon','Brando',2,'Don Vito Corleone'), ('Christian','Bale',3,'Bruce Wayne / Batman'), ('Al','Pacino',4,'Michael'), ('Henry','Fonda',5,'Juror 8'), ('Liam','Neeson',6,'Oskar Schindler'), ('Elijah','Wood',7,'Frodo'), ('John','Travolta',8,'Vincent Vega'), ('Elijah','Wood',9,'Frodo'), ('Clint','Eastwood',10,'Blondie'); -- Fiktiva värden Tabell "WORK_ROLES" INSERT INTO WORK_ROLES VALUES (1,1,'Lagerarbetare'), (2,1,'Kassör'), (3,1,'Städtekniker'), (4,1,'Lagerarbetare'), (5,2,'Lagerarbetare'), (6,2,'Kassör'), (7,2,'Städtekniker'), (8,2,'Lagerarbetare'), (9,3,'Lagerarbetare'), (10,3,'Kassör'), (11,3,'Städtekniker'), (12,3,'Lagerarbetare'), (13,4,'Lagerarbetare'), (14,4,'Kassör'), (15,4,'Städtekniker'), (16,4,'Lagerarbetare'); -- Fiktiva värden Tabell "STORING",(FilmNr,ButikNr,HyllNr,Kopior,Pris) INSERT INTO STORING VALUES (1,1,1,5,50), (2,1,1,5,70), (3,1,1,5,60), (4,1,1,5,55), (5,1,1,5,55), (6,1,1,5,45), (7,1,1,5,45), (8,1,1,5,40), (9,1,1,5,60), (10,1,1,5,50), (1,2,1,5,55), (2,2,1,5,70), (3,2,1,5,65), (4,2,1,5,40), (5,2,1,5,45), (6,2,1,5,40), (7,2,1,5,45), (8,2,1,5,50), (9,2,1,5,65), (10,2,1,5,60), (1,3,1,5,55), (2,3,1,5,55), (3,3,1,5,40), (4,3,1,5,40), (5,3,1,5,45), (6,3,1,5,40), (7,3,1,5,75), (8,3,1,5,70), (9,3,1,5,65), (10,3,1,5,60), (1,4,1,5,45), (2,4,1,5,40), (3,4,1,5,70), (4,4,1,5,65), (5,4,1,5,45), (6,4,1,5,55), (7,4,1,5,45), (8,4,1,5,40), (9,4,1,5,60), (10,4,1,5,55); -- Fiktiva värden Tabell "RENTING",(FilmNr,MedlemNr,HyrNr,HyrStartDatum,HyrReturDatum) INSERT INTO RENTING VALUES (1,1,1,'2023-02-14','2023-02-21'), (2,1,2,'2023-02-14','2023-02-21'), (3,1,3,'2023-02-14','2023-02-21'), (4,1,4,'2023-02-14','2023-02-21'), (5,1,5,'2023-02-14','2023-02-21'), (6,1,6,'2023-02-14','2023-02-21'), (7,1,7,'2023-02-14','2023-02-21'), (8,1,8,'2023-02-14','2023-02-21'), (9,1,9,'2023-02-14','2023-02-21'), (10,1,10,'2023-02-14','2023-02-21'), (1,1,11,'2023-02-14','2023-02-22'), (2,1,12,'2023-02-14','2023-02-23'), (3,1,13,'2023-02-14','2023-02-24'), (4,1,14,'2023-02-14','2023-02-25'), (5,1,15,'2023-02-14','2023-02-26'), (6,1,16,'2023-02-14','2023-02-27'), (7,1,17,'2023-02-14','2023-02-28'), (8,1,18,'2023-02-14','2023-02-28'), (9,1,19,'2023-02-14','2023-02-28'), (10,1,20,'2023-02-14','2023-02-28'); SET foreign_key_checks = 1;

Dold text

Imorgon inleds redan Labb 3 i Databas-kursen vilket kommer att vara Normalisering och Normalformer!

Moment 4 i Webbutveckling II är att skapa en mycket simpel nyhets- eller bloggsida med registrering och inloggning för en person i taget som ska kunna posta och visa de senaste inlagda inläggen. Projektuppgiften är en större och mer omfattande variant av denna.

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Jag skulle rekommendera att inte använda versaler för tabellnamn & kolumner just för att hålla isär det från SQL-syntaxen (som inte heller "måste" vara just versaler, men det blir MYCKET tydligare att se vad som faktiskt är vad).

T.ex.

CREATE TABLE Employee ...

Visa signatur

NZXT H510 Flow MSI B450 Tomahawk MAX
AMD Ryzen 5800X3D RX 7900XTX Kingston Fury 64GB

Permalänk
Skrivet av Pamudas:

Jag skulle rekommendera att inte använda versaler för tabellnamn & kolumner just för att hålla isär det från SQL-syntaxen (som inte heller "måste" vara just versaler, men det blir MYCKET tydligare att se vad som faktiskt är vad).

T.ex.

CREATE TABLE Employee ...

Jag förstår! Jag tar mig till det och undviker versala tabellnamn hädanefter!

Har du förresten någon aning om varför jag var tvungen att i CONSTRAINTS.sql-filen lägga till "ADD UNIQUE" i efterhand för vissa tabeller? Exempelvis provade jag att göra blivande primärnycklar UNIQUE (t.ex. Store_number INT NOT NULL UNIQUE) men då kunde jag i Constraints inte lägga till PRIMARY KEY för samma attribut.

Däremot kunde jag - som jag gjort - först skapat tabellen med Store_number som sedan först tilldelats PRIMARY KEY och sedan UNIQUE. Detta är för att i vissa sambandstabeller ska en kolumn i en sammansatt primärnyckel vara unik. Exempelvis i RENTING-tabellen där en medlem kan hyra flera filmer samtidigt men för varje film så blir det ett unikt hyrnummer.

Exempel:
1,1,1 = Film 1 hyrs av medlem 1 med Uthyrningsnummer 1
2,1,2 = Film 2 hyrs av medlem 1 med Uthyrningsnummer 2
2,1,3 = Film 2 hyrs igen av medlem 1 med Uthyrningsnummer 3
2,1,1 = Uthyrningsnummer redan taget, går ej!
Osv...

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Jag är äntligen färdig med Databas-kursens Labb 2 som handlade om skapandet av SQL-tabeller, infogandet av data i dessa tabeller såväl som SQL-frågor från dessa för att knyta ihop säcken.

Nedanför finns alla SQL-filer för den som själv vill provköra det i en DBMS med SQL-stöd:

Basrelationer

-- Basrelation från entitet "Butik" på engelska STORE(Store_number(PK),Address_street,Address_city,Address_housenumber,Address_zipcode,Telephone_number,Store_boss_name,Store_name) ---------------------------------------------------------- -- Basrelation från entitet "Anställd" på engelska EMPLOYEE(Employment_number(PK),Employed_at(FK),Firstname,Lastname,Salary) -- Basrelationskopplingar -- Employed_at(FK) = Butik ---------------------------------------------------------- -- Basrelation från entitet "Medlem" på engelska MEMBER(Member_number(PK),Store_number(FK),Registered_date,Member_firstname,Member_lastname,Member_address_street,Member_address_city,Member_address_housenumber,Member_address_zipcode) -- Basrelationskopplingar -- Store_number(FK) = Butik ---------------------------------------------------------- -- Basrelation från entitet "Film" på engelska MOVIE(Movie_number(PK),Movie_name,Movie_length,Movie_main_category) ---------------------------------------------------------- -- Basrelation från sambandstyp "Hyr" på engelska RENTING((Movie_number+Member_number+Renting_number(PK)),Movie(FK),Member_number(FK),Renting_number,Renting_start_date,Renting_return_date) -- Basrelationskopplingar -- Movie(FK) = Film -- Member_number(FK) = Medlem ---------------------------------------------------------- -- Basrelation från sambandstyp "som_lagrar" på engelska STORING((Movie_number+Store_number+Shelf_number(PK)),Store_number(FK),Movie(FK),Shelf_number,Copies_per_movie,Renting_price) -- Basrelationskopplingar -- Store_number(FK) = Butik -- Movie_number(FK) = Film ---------------------------------------------------------- -- Basrelation från sambandstyp "har_en_arbetsroll_på" på engelska WORK_ROLES((Employment_number+Store_number(PK)),Employment_number(FK),Employed_at(FK),Working_role) -- Basrelationskopplingar -- Employment_number(FK) = Anställd -- Employed_at(FK) = Butik ---------------------------------------------------------- -- Basrelation från flervärdesattribut "Regissör" på engelska DIRECTOR(Movie(FK),First_name,Last_name) -- Basrelationskopplingar -- Movie_number(FK) = Film ---------------------------------------------------------- -- Basrelation från flervärdesattribut "Skådespelare" på engelska ACTOR(Movie(FK),First_name,Last_name,Movie_role) -- Basrelationskopplingar -- Movie_number(FK) = Film

Dold text

Labb2_CREATES.sql

CREATE DATABASE IF NOT EXISTS Labb2; USE Labb2; -- CREATING TABLES -- Tabell "Butik" CREATE TABLE STORE ( Store_number INT NOT NULL, -- Make Primary Key in Constraints Address_city VARCHAR(64) NOT NULL, Address_street VARCHAR(64) NOT NULL, Address_housenumber INT(6) NOT NULL, Address_zipcode INT(5) NOT NULL, Telephone_number INT(12) NOT NULL, Store_boss_name VARCHAR(64) NOT NULL, Store_name VARCHAR(64) NOT NULL); -- Tabell "Anställd" CREATE TABLE EMPLOYEE ( Employment_number INT NOT NULL, -- Make Primary Key in Constraints Firstname VARCHAR(64) NOT NULL, Lastname VARCHAR(64) NOT NULL, Salary DECIMAL(8,2) NOT NULL, Employed_at INT NOT NULL); -- Foreign key to Table "WORK_ROLES" -- Tabell "Medlem" CREATE TABLE MEMBER ( Member_number INT NOT NULL, -- Make Primary Key in Constraints Registered_date DATE NOT NULL, Member_firstname VARCHAR(64) NOT NULL, Member_lastname VARCHAR(64) NOT NULL, Member_address_street VARCHAR(64) NOT NULL, Member_address_city VARCHAR(64) NOT NULL, Member_address_housenumber VARCHAR(64) NOT NULL, Member_address_zipcode VARCHAR(64) NOT NULL, Store_number INT NOT NULL); -- Foreign key to Table "STORE" -- Tabell "Film" CREATE TABLE MOVIE ( Movie_number INT NOT NULL, -- Make Primary Key in Constraints Movie_name VARCHAR(64) NOT NULL, Movie_length INT(3) NOT NULL, Movie_main_category VARCHAR(64) NOT NULL); -- Tabell "Hyr" CREATE TABLE RENTING ( Movie_number INT NOT NULL, -- Foreign key to Table "MOVIE" Member_number INT NOT NULL, -- Foreign key to Table "MEMBER" Renting_number INT NOT NULL, -- Make Composite Key ?? Renting_start_date DATE NOT NULL, Renting_return_date DATE NOT NULL); -- Tabell "som_lagrar" CREATE TABLE STORING ( Movie_number INT NOT NULL, -- Foreign key to Table "MOVIE" Store_number INT NOT NULL, -- Foreign key to Table "STORE" Shelf_number INT NOT NULL, -- Make Composite Key ?? Copies_per_movie INT NOT NULL, Renting_price INT NOT NULL); -- Tabell "har_en_arbetsroll_på" CREATE TABLE WORK_ROLES ( Employment_number INT NOT NULL, -- Make Composite Key ?? Store_number INT NOT NULL, -- Make Composite Key ?? Working_role VARCHAR(64) NOT NULL); -- Tabell "Regissör" CREATE TABLE DIRECTOR ( First_name VARCHAR(64) NOT NULL, Last_name VARCHAR(64) NOT NULL, Movie_number INT NOT NULL); -- Foreign key to Table "MOVIE" -- Tabell "Skådespelare" CREATE TABLE ACTOR ( First_name VARCHAR(64) NOT NULL, Last_name VARCHAR(64) NOT NULL, Movie_number INT NOT NULL, -- Foreign key to Table "MOVIE" Movie_role VARCHAR(64) NOT NULL);

Dold text

Labb2_CONSTRAINTS.sql

-- SET PRIMARY KEYS FOR TABLES -- Tabell "Butik" ALTER TABLE STORE ADD CONSTRAINT Store_pk PRIMARY KEY (Store_number); ALTER TABLE STORE ADD UNIQUE (Store_number); -- Tabell "Anställd" ALTER TABLE EMPLOYEE ADD CONSTRAINT Employee_pk PRIMARY KEY (Employment_number); -- Tabell "Medlem" ALTER TABLE MEMBER ADD CONSTRAINT Member_pk PRIMARY KEY (Member_number); -- Tabell "Film" ALTER TABLE MOVIE ADD CONSTRAINT Movie_pk PRIMARY KEY (Movie_number); -- Tabell "har_en_arbetsroll_på" ALTER TABLE WORK_ROLES ADD CONSTRAINT WorkRoles_pk PRIMARY KEY (Employment_number, Store_number); ALTER TABLE WORK_ROLES ADD UNIQUE (Employment_number); -- Tabell "som_lagrar" ALTER TABLE STORING ADD CONSTRAINT Storing_pk PRIMARY KEY (Movie_number, Store_number, Shelf_number); -- Tabell "Film" ALTER TABLE RENTING ADD CONSTRAINT Renting_pk PRIMARY KEY (Movie_number, Member_number, Renting_number); ALTER TABLE RENTING ADD UNIQUE (Renting_number); -- SET FOREIGN KEYS FOR TABLES -- Tabell "Anställd" ALTER TABLE EMPLOYEE ADD CONSTRAINT Employee_Store_fk FOREIGN KEY (Employed_at) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "Medlem" ALTER TABLE MEMBER ADD CONSTRAINT Member_at_fk FOREIGN KEY (Store_number) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "har_en_arbetsroll_på" ALTER TABLE WORK_ROLES ADD CONSTRAINT WR_Emp_fk FOREIGN KEY (Employment_number) REFERENCES EMPLOYEE(Employment_number) ON UPDATE CASCADE; ALTER TABLE WORK_ROLES ADD CONSTRAINT WR_Store_fk FOREIGN KEY (Store_number) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "Hyr" ALTER TABLE RENTING ADD CONSTRAINT RentMember_at_fk FOREIGN KEY (Member_number) REFERENCES MEMBER(Member_number) ON UPDATE CASCADE; ALTER TABLE RENTING ADD CONSTRAINT RentMovie_at_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE; -- Tabell "som_lagrar" ALTER TABLE STORING ADD CONSTRAINT StoringMovie_at_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE; ALTER TABLE STORING ADD CONSTRAINT StoringStore_at_fk FOREIGN KEY (Store_number) REFERENCES STORE(Store_number) ON UPDATE CASCADE; -- Tabell "Regissör" ALTER TABLE DIRECTOR ADD CONSTRAINT Director_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE; -- Tabell "Skådespelare" ALTER TABLE ACTOR ADD CONSTRAINT Actor_fk FOREIGN KEY (Movie_number) REFERENCES MOVIE(Movie_number) ON UPDATE CASCADE;

Dold text

Labb2_QUERIES.sql

-- SQL-Fråga:"Visa alla som INTE heter -- Stekmark i efternamn från tabellen MEMBER" SELECT * FROM MEMBER where Member_lastname!='Stekmark'; -- SQL-Fråga:"Visa alla från tabellen MEMBER där förnamnet -- innehåller ett stort eller litet F i början av namnet" SELECT * FROM MEMBER where Member_firstname LIKE 'F%'; -- SQL-Fråga:"Visa alla som är registrerade i Januari 2023" SELECT Registered_date FROM MEMBER WHERE Registered_date BETWEEN '2023-01-01' AND '2023-01-31'; -- SQL-Fråga:"Visa vem som har lägst lön i -- varje Butik, gruppera efter Butiks-nr" SELECT min(Salary) FROM EMPLOYEE GROUP BY Employed_at; -- SQL-Fråga:"Gruppera filmneras körtid från lägst -- till högst så länge de är över 120 minuter långa" SELECT * FROM MOVIE GROUP BY Movie_length HAVING Movie_length > 120; -- SQL-Fråga:"Gruppera filmneras körtid från lägst -- till högst så länge de är en Kriminalfilm" SELECT * FROM MOVIE GROUP BY Movie_length HAVING Movie_main_category="Kriminalfilm"; -- SQL-Fråga:"Visa alla från tabellen MEMBER -- i bokstavsordning A till Ö för förnamn" SELECT * FROM MEMBER ORDER BY Member_firstname asc; -- SQL-Fråga:"Ändra lönen till 99999 kr för anställd nr 12" UPDATE EMPLOYEE SET SALARY=99999 WHERE Employment_number=12; -- SQL-Fåga:"Visa vilka anställda -- som har över 20 000 kronor i lön" SELECT * FROM EMPLOYEE WHERE Employment_number IN (SELECT Employment_number FROM EMPLOYEE WHERE Salary > 20000);

Dold text

Labb2_INSERTS.sql

SET foreign_key_checks = 0; -- Fiktiva värden Tabell "STORE" INSERT INTO STORE VALUES (1,'Stekstaden','Schysstvägen','69','86035','0694201340','Jörgen Jönsson','Hyra Film AB'), (2,'Stekstaden','Drakvägen','420','86035','0694201338','Jörgen Jönsson','Hyra Film AB'), (3,'Stekstaden','Mustvägen','1337','86035','0694201337','Jörgen Jönsson','Hyra Film AB'), (4,'Stekstaden','Fiolvägen','1','86035','0694201339','Jörgen Jönsson','Hyra Film AB'); -- Fiktiva värden Tabell "EMPLOYEE" INSERT INTO EMPLOYEE VALUES (1,'Stefan','Olsson',13200,1), (2,'Ulla','Jonsson',11369,1), (3,'Alban','Sheesh',15420,1), (4,'Inger','Yolo',13469,1), (5,'Johan','Stenmark',13350,2), (6,'Pelle','Borg',13309,2), (7,'Elin','Salming',13400,2), (8,'Inga','Rövhalt',16461,2), (9,'Viola','Rosenspira',14332,3), (10,'Magnus','Meckberg',15665,3), (11,'Kristoffer','Själqvist',13651,3), (12,'Pontus','Pilatus',16464,3), (13,'Krister','Hansson',11210,4), (14,'Inga-Britt','Ztek',17569,4), (15,'Annicka','Markbock',19999,4), (16,'Markus','Häänssåån',12222,4); -- Fiktiva värden Tabell "MEMBER" INSERT INTO MEMBER VALUES (1,'2023-01-01','Monika','Stekmark','Stekarvägen','Stekstaden','420','86039',1), (2,'2023-01-08','Alban','Albansson','Kråkvägen','Stekstaden','10','86039',1), (3,'2023-01-09','Mulle','Meck','Chokladvägen','Stekstaden','33','86039',2), (4,'2023-01-10','Agnes','Figenstia','Potatisvägen','Stekstaden','40','86039',2), (5,'2023-01-15','Urban','Rundqvist','Fläskvägen','Stekstaden','20','86039',3), (6,'2023-01-20','Fabian','Sibkvast','Stekvägen','Stekstaden','4','86039',3), (7,'2023-01-25','Jannicka','Almqvist','Najsvägen','Stekstaden','69','86039',4), (8,'2023-02-05','Thomas','Quist','Majsvägen','Stekstaden','1337','86039',4); -- Fiktiva värden Tabell "MOVIE" INSERT INTO MOVIE VALUES (1,'The Shawshank Redemption',142,'Drama'), (2,'The Godfather',175,'Kriminalfilm'), (3,'The Dark Knight',152,'Action'), (4,'The Godfather Part II',202,'Kriminalfilm'), (5,'12 Angry Men',96,'Kriminalfilm'), (6,'Schindler''s List',142,'Drama'), (7,'The Lord of the Rings: The Return of the King',201,'Actionfilm'), (8,'Pulp Fiction',154,'Kriminalfilm'), (9,'The Lord of the Rings: The Fellowship of the Ring',178,'Drama'), (10,'The Good, the Bad and the Ugly',178,'Västernfilm'); -- Fiktiva värden Tabell "DIRECTOR" INSERT INTO DIRECTOR VALUES ('Frank','Darabont',1), ('Francis Ford','Coppola',2), ('Christopher','Nolan',3), ('Francis Ford','Coppola',4), ('Sidney','Lumet',5), ('Steven','Spielberg',6), ('Peter','Jackson',7), ('Quentin','Tarantino',8), ('Peter','Jackson',9), ('Sergio','Leone',10); -- Fiktiva värden Tabell "ACTOR" INSERT INTO ACTOR VALUES ('Tim','Robbin',1,'Andy Dufresne'), ('Marlon','Brando',2,'Don Vito Corleone'), ('Christian','Bale',3,'Bruce Wayne / Batman'), ('Al','Pacino',4,'Michael'), ('Henry','Fonda',5,'Juror 8'), ('Liam','Neeson',6,'Oskar Schindler'), ('Elijah','Wood',7,'Frodo'), ('John','Travolta',8,'Vincent Vega'), ('Elijah','Wood',9,'Frodo'), ('Clint','Eastwood',10,'Blondie'); -- Fiktiva värden Tabell "WORK_ROLES" INSERT INTO WORK_ROLES VALUES (1,1,'Lagerarbetare'), (2,1,'Kassör'), (3,1,'Städtekniker'), (4,1,'Lagerarbetare'), (5,2,'Lagerarbetare'), (6,2,'Kassör'), (7,2,'Städtekniker'), (8,2,'Lagerarbetare'), (9,3,'Lagerarbetare'), (10,3,'Kassör'), (11,3,'Städtekniker'), (12,3,'Lagerarbetare'), (13,4,'Lagerarbetare'), (14,4,'Kassör'), (15,4,'Städtekniker'), (16,4,'Lagerarbetare'); -- Fiktiva värden Tabell "STORING",(FilmNr,ButikNr,HyllNr,Kopior,Pris) INSERT INTO STORING VALUES (1,1,1,5,50), (2,1,1,5,70), (3,1,1,5,60), (4,1,1,5,55), (5,1,1,5,55), (6,1,1,5,45), (7,1,1,5,45), (8,1,1,5,40), (9,1,1,5,60), (10,1,1,5,50), (1,2,1,5,55), (2,2,1,5,70), (3,2,1,5,65), (4,2,1,5,40), (5,2,1,5,45), (6,2,1,5,40), (7,2,1,5,45), (8,2,1,5,50), (9,2,1,5,65), (10,2,1,5,60), (1,3,1,5,55), (2,3,1,5,55), (3,3,1,5,40), (4,3,1,5,40), (5,3,1,5,45), (6,3,1,5,40), (7,3,1,5,75), (8,3,1,5,70), (9,3,1,5,65), (10,3,1,5,60), (1,4,1,5,45), (2,4,1,5,40), (3,4,1,5,70), (4,4,1,5,65), (5,4,1,5,45), (6,4,1,5,55), (7,4,1,5,45), (8,4,1,5,40), (9,4,1,5,60), (10,4,1,5,55); -- Fiktiva värden Tabell "RENTING",(FilmNr,MedlemNr,HyrNr,HyrStartDatum,HyrReturDatum) INSERT INTO RENTING VALUES (1,1,1,'2023-02-14','2023-02-21'), (2,1,2,'2023-02-14','2023-02-21'), (3,1,3,'2023-02-14','2023-02-21'), (4,1,4,'2023-02-14','2023-02-21'), (5,1,5,'2023-02-14','2023-02-21'), (6,1,6,'2023-02-14','2023-02-21'), (7,1,7,'2023-02-14','2023-02-21'), (8,1,8,'2023-02-14','2023-02-21'), (9,1,9,'2023-02-14','2023-02-21'), (10,1,10,'2023-02-14','2023-02-21'), (1,1,11,'2023-02-14','2023-02-22'), (2,1,12,'2023-02-14','2023-02-23'), (3,1,13,'2023-02-14','2023-02-24'), (4,1,14,'2023-02-14','2023-02-25'), (5,1,15,'2023-02-14','2023-02-26'), (6,1,16,'2023-02-14','2023-02-27'), (7,1,17,'2023-02-14','2023-02-28'), (8,1,18,'2023-02-14','2023-02-28'), (9,1,19,'2023-02-14','2023-02-28'), (10,1,20,'2023-02-14','2023-02-28'); SET foreign_key_checks = 1;

Dold text

Imorgon inleds redan Labb 3 i Databas-kursen vilket kommer att vara Normalisering och Normalformer!

Moment 4 i Webbutveckling II är att skapa en mycket simpel nyhets- eller bloggsida med registrering och inloggning för en person i taget som ska kunna posta och visa de senaste inlagda inläggen. Projektuppgiften är en större och mer omfattande variant av denna.

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Enligt vad jag läst bör man inte upprepa sig i kolumn-namnen dvs i tabellen Movie t ex behöver man inte prefixa med movie. Sen brukar man väl döpa tabeller i plural, men finns säkert massa tyckande om sånt här precis som med allt annat inom IT 😁

Själv föredrar jag PascalCase på kolumner men jag kör SQL Server och verkar mer standard där medan MySQL och andra verkar gilla de fula understrecken 😁

Permalänk

Slutbetyg på DT200G-kursen och Labb 2 godkänd i Databaser

Slutbetyg i DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)

Godkänd i Labb 2 i Databaser tack vare bortsett fel som först krävde komplettering

Jag skriver tydligen "föredömligt välskrivna" kommentarer trots att de är på svenska!

Nu fortsätter jag med Moment 4 (Databasanslutning) i Webbutveckling II och Labb 3 (😱Normalisering & normalformer😱) i Databaser.

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
✔️(B) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

"Framtidsbloggen Beta" lanserad (Moment 4 inlämnad snart)

Jag är nu färdig med vad jag kallar för "Framtidsbloggen Beta" vilket är en förenklad bloggversion av Projektuppgiften i Webbutveckling II-kursen (en PHP-kurs i grund och botten). Jag har också fått större insikter inom Databaser beträffande 1NF, 2NF och 3NF. Labb 3 där bör jag få färdig/inlämnad på nytt tills imorgon.

Du kan prova "beta-bloggen" här:
"Framtidsbloggen Beta (Moment 4)"

Den har följande funktionalitet:
- Registrering & inloggning (unikt användarnamn och e-post, användarnamn används för inloggning så hitta på mejl om du vill)
- Inmatningsvalidering (måste ha rätt syntax för användarnamn, e-post och lösenord)
- Lösenord lagras med password_hash()
- Visning av andras blogginlägg utan inloggningskrav
- Skriva, ändra och ta bort egna inlägg men ej andras (prova gärna dock!)
- Skydd mot SQL Injections vid alla inmatningar (prova gärna dock att försöka kringgå dem!)
- Tydliga röda felmeddelanden vid användarfel
- Tydliga gröna meddelanden vid framgång
- Övriga fräsiga gröna färger

Känd CSS-bugg jag arbetar på att lösa/spöa:
- När du sparar inloggningsuppgifter tills nästa gång så fungerar inte CSS som den ska. Något med hur JS-koden tolkar ifyllda fält (inputtextfield.value !== "") när de har fyllts i av ren automatik än av användaren. JS-skriptet läser först in alla textfält efter DOMContentLoaded så det är underligt att den inte tolkar automatiska ifyllda fält som fyllda med strängar. Arbetar med att lösa detta, men inget krav i uppgiften för Moment 4 som tur var!

Jag ska nu fixa ER- & UML-diagram för denna Moment 4-uppgift så kan jag officiellt lämna in den. Men webbplatsen i momentet i sig är färdig för "testkörning" i den utsträckning som det går!

På återseende!

Mvh,
WKL.

P.S. "Framtidsbloggen 2.0" (Projektuppgift i Webbutveckling II) är nu under utveckling!
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
✔️(B) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Jag är nu färdig med vad jag kallar för "Framtidsbloggen Beta" vilket är en förenklad bloggversion av Projektuppgiften i Webbutveckling II-kursen (en PHP-kurs i grund och botten). Jag har också fått större insikter inom Databaser beträffande 1NF, 2NF och 3NF. Labb 3 där bör jag få färdig/inlämnad på nytt tills imorgon.

Du kan prova "beta-bloggen" här:
"Framtidsbloggen Beta (Moment 4)"

Den har följande funktionalitet:
- Registrering & inloggning (unikt användarnamn och e-post, användarnamn används för inloggning så hitta på mejl om du vill)
- Inmatningsvalidering (måste ha rätt syntax för användarnamn, e-post och lösenord)
- Lösenord lagras med password_hash()
- Visning av andras blogginlägg utan inloggningskrav
- Skriva, ändra och ta bort egna inlägg men ej andras (prova gärna dock!)
- Skydd mot SQL Injections vid alla inmatningar (prova gärna dock att försöka kringgå dem!)
- Tydliga röda felmeddelanden vid användarfel
- Tydliga gröna meddelanden vid framgång
- Övriga fräsiga gröna färger

Känd CSS-bugg jag arbetar på att lösa/spöa:
- När du sparar inloggningsuppgifter tills nästa gång så fungerar inte CSS som den ska. Något med hur JS-koden tolkar ifyllda fält (inputtextfield.value !== "") när de har fyllts i av ren automatik än av användaren. JS-skriptet läser först in alla textfält efter DOMContentLoaded så det är underligt att den inte tolkar automatiska ifyllda fält som fyllda med strängar. Arbetar med att lösa detta, men inget krav i uppgiften för Moment 4 som tur var!

Jag ska nu fixa ER- & UML-diagram för denna Moment 4-uppgift så kan jag officiellt lämna in den. Men webbplatsen i momentet i sig är färdig för "testkörning" i den utsträckning som det går!

På återseende!

Mvh,
WKL.

P.S. "Framtidsbloggen 2.0" (Projektuppgift i Webbutveckling II) är nu under utveckling!
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
✔️(B) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Kul att se att det går bra, riktigt fancy effekt när man byter sida

Iom att vi blev inbjudna att testa SQL injections så testade jag även XSS på sajten. Gå in på "6. Moment 3" för att se den in action. Du kan kolla i json filen om du undrar vad som försigår.

För att förhindra att detta sker så bör du kolla på nåt i stil med detta https://stackoverflow.com/questions/1996122/how-to-prevent-xs...

Permalänk
Skrivet av martengooz:

Kul att se att det går bra, riktigt fancy effekt när man byter sida

Iom att vi blev inbjudna att testa SQL injections så testade jag även XSS på sajten. Gå in på "6. Moment 3" för att se den in action. Du kan kolla i json filen om du undrar vad som försigår.

För att förhindra att detta sker så bör du kolla på nåt i stil med detta https://stackoverflow.com/questions/1996122/how-to-prevent-xs...

Tack för den viktiga inputen!

Jag har inaktiverat inmatningsfälten där (dvs., de kör ingen echo med data som tagits emot) nu.

I Moment 4 har jag riktig valideringskontroll med Anti-SQL Injection kod så!

EDIT: Aha, ser nu vad jag ska göra. Ska prova det nu istället. Då skriver den ut som vanlig "RawStringText" istället för att göra en kodtolkning?

EDIT2: Fråga: Jag har gjort följande kod nu:

else { // Loop som skriver ut blogginlägg foreach($currentBlogpost as $blogpost){ ?> <h3 style="margin-top:20px;"><? echo htmlspecialchars($blogpost['blogpost_title']);?></h3> <p style="font-size:0.9rem; font-weight:bold; margin-bottom:5px;"><?= "Postad: " . blogPublishDate($blogpost['blogpost_created']) . $blogpost['blogpost_created_by'];?></p> <p style="font-size:1rem;"><?echo htmlspecialchars($blogpost['blogpost_text']) ?></p> <p style="text-align:right;">

Men jag antar att jag behöver htmlspecialchars i ALLA utskrifter och även vid inmatningar när de ska lagras i $_POST och $_GET? Exempelvis nu så verkar jag få JS körd vid inmatning (t.ex. skapa nytt blogginlägg).

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Tack för den viktiga inputen!

Jag har inaktiverat inmatningsfälten där (dvs., de kör ingen echo med data som tagits emot) nu.

I Moment 4 har jag riktig valideringskontroll med Anti-SQL Injection kod så!

EDIT: Aha, ser nu vad jag ska göra. Ska prova det nu istället. Då skriver den ut som vanlig "RawStringText" istället för att göra en kodtolkning?

Mvh,
WKL.

Ajjemen, man måste se till att det inte skickas ut som en script tagg, utan som vanlig text bara Annars kommer webbläsaren köra den koden och dumma saker kan hända.

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Men jag antar att jag behöver htmlspecialchars i ALLA utskrifter och även vid inmatningar när de ska lagras i $_POST och $_GET? Exempelvis nu så verkar jag få JS körd vid inmatning (t.ex. skapa nytt blogginlägg).

Det är något man behöver tänka på rent generellt. ALL data som kommer från användare eller externa parter måste valideras och se till att det inte körs som kod när det inte ska. Oavsett om det är en kommentar på ett blogginlägg, användarnamn, favoritemoji på profilen eller värde på vilken säkerhetsfråga man har för lösenordsåterställning. Allt som kommer från frontenden eller användare kan användas för att göra dumma saker

Permalänk
Skrivet av martengooz:

Ajjemen, man måste se till att det inte skickas ut som en script tagg, utan som vanlig text bara Annars kommer webbläsaren köra den koden och dumma saker kan hända.

Hm, jag tog bort echo-funktionerna i "Moment 3", "" och "Formulär" och då körs ingen fulkod (<script>alert("Test");</script>).

Men den gör det ändå i foreach-loopen i Moment 4. Jag undrar om det är för att jag först läser in hela arrayen i en variabel som sedan läses ut av foreach-loopen? Bör jag ändra "Collation" i DBMS:en?

Eller bör jag vid valideringsstegen innan data skrivs in i databasen omvandlas till de mindre "farliga" tecknen som &lt; och &gt; ?

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Hm, jag tog bort echo-funktionerna i "Moment 3", "" och "Formulär" och då körs ingen fulkod (<script>alert("Test");</script>).

Men den gör det ändå i foreach-loopen i Moment 4. Jag undrar om det är för att jag först läser in hela arrayen i en variabel som sedan läses ut av foreach-loopen? Bör jag ändra "Collation" i DBMS:en?

Eller bör jag vid valideringsstegen innan data skrivs in i databasen omvandlas till de mindre "farliga" tecknen som &lt; och &gt; ?

Mvh,
WKL.

Generellt bör man "sanitera" input det första man gör när man tar emot data i backenden (https://stackoverflow.com/questions/1336776/xss-filtering-fun...). Då slipper man att dumma saker sparas i databasen från första början.

Permalänk
Skrivet av martengooz:

Generellt bör man "sanitera" input det första man gör när man tar emot data i backenden (https://stackoverflow.com/questions/1336776/xss-filtering-fun...). Då slipper man att dumma saker sparas i databasen från första början.

Nu fick jag till echo-biten:

<?= htmlspecialchars($blogpost['blogpost_title'], ENT_QUOTES, 'UTF-8');?>

Men det fungerar inte om jag använder:

<? echo htmlspecialchars($blogpost['blogpost_title'], ENT_QUOTES, 'UTF-8');?>

Och det är ju underligt för <?= ?> är ju en förkortning för: <?php echo ?> om jag inte misstagit mig?

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Stort tack till den etiska provhackaren @martengooz som visade nybörjarmisstag jag gjort som blivande webbutvecklare med avsaknad av skydd mot XSS!

Jag förstår dock inte varför <?= htmlspecialchars("<script>alert('Test!');</script>", ENT_QUOTES, 'UTF-8');?> fungerar men inte <?php echo htmlspecialchars("<script>alert('Test!');</script>", ENT_QUOTES, 'UTF-8');?> då förstnämnda ska vara en förkortad version av sistnämnda? 🤔

Nu kan inte "SkriptUngen" slå till igen! "Framtidsbloggen 2.0" är räddad tills vidare!

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Källkod till PHP Moment 4 & Labb 3 i Databaser troligen godkänd! [Del 1 av 4]

Bild på Labb 3 med 3NF ER-Diagram

[Del 1 av 4]: Källkod till PHP Moment 4 (skapa mapparna "css", "include" och "class" inuti "include" och kör sedan "install.php" och sedan "index.php"):

css/styles.css

/* =============================== NORMALIZING + FONTS - All Pages =============================== */ * { padding: 0; margin: 0; box-sizing: border-box; font-size: 1rem; font-family: 'Orbitron', sans-serif; } /* ================== <BODY> - All Pages ================== */ body{ width: 100%; margin: 0 auto; } /* ======================== FONT ELEMENTS - Standard ======================== */ h1{ font-size: 3rem; color: black; } h2{ font-size: 2.1rem; text-align: center; margin-bottom: 20px; } h1,h2,h3{ font-family: 'Orbitron'; font-weight: 800; } h3{ font-size: 1.4rem; text-align: center; margin-bottom: 10px; } h4{ font-size: 1.2rem; margin-bottom: 5px; } span, i, p{ font-size: 1.3rem; line-height: 1.5; max-width: 60em; font-weight: lighter; } .blogpost-feature{ overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } /* ======================== .main-layout div ======================== */ .main-layout{ min-height: 100vh; min-height: 100dvh; display: grid; grid-template-rows: auto 1fr auto; background-color: rgb(16, 16, 16); color: black; } .questions, main > h2{ color: white; } /* ======================== <nav> & .menu-btn ======================== */ .menu-btn{ display: inline-block; width: 200px; padding: 20px; font-size: 20px; font-weight: 700; cursor: pointer; border: none; background: #383b3a; color: #1bbb85; border: 2px solid #1bbb85; border-radius: 5px; text-align: center; margin: 10px; transition: background 500ms ease-out, color 500ms ease-out; } .menu-btn2{ display: inline-block; width: 158px; padding: 10px; font-size: 16px; font-weight: 700; cursor: pointer; border: none; background: #383b3a; color: #1bbb85; border: 2px solid #1bbb85; border-radius: 5px; text-align: center; margin: 10px; transition: background 500ms ease-out, color 500ms ease-out; } .current-btn-admin, .current-btn{ box-shadow: 5px 5px 5px #1bbb85, 5px -5px 5px #1bbb85, -5px 5px 5px #1bbb85, -5px -5px 5px #1bbb85; } a{ color: #1bbb85; text-decoration: none; transition: all 0.5s ease-in-out; } .menu-btn2:hover, .menu-btn2:focus, .menu-btn:hover, .menu-btn:focus{ cursor: pointer; background: #1bbb85; border-radius: 5px; } .deleteButtons:hover, .deleteButtons:focus, input[type="submit"]:hover, input[type="submit"]:focus{ cursor: pointer; background: #1bbb85; color: white; } .menu-btn2:hover > a, .menu-btn2:focus > a, .menu-btn:hover > a, .menu-btn:focus > a{ color: #383b3a; } #nav-ul{ margin-top: 1rem; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-evenly; } #nav-ul2{ display: flex; flex-direction: row; flex-wrap: wrap; justify-content: center; } /* =========================== <main> =========================== */ main{ margin: 0 auto; padding-left: 10%; padding-right: 10%; } /* =========================== HEADER & FOOTER SETTINGS =========================== */ header{ background: rgb(16, 16, 16); background: linear-gradient(0deg, rgba(16, 16, 16,1) 0%, rgba(27,187,133,1) 35%, rgba(128,31,125,1) 100%); } footer{ background: rgb(16, 16, 16); background: linear-gradient(180deg, rgba(16, 16, 16,1) 0%, rgba(27,187,133,1) 35%, rgba(128,31,125,1) 100%); } header{ padding: 3%; text-align: center; } footer{ padding: 3%; text-align: center; font-weight: bold; } #footer-text{ font-size: 0.9rem; line-height: 1.5; color: black; } .main-layout{ min-width: 390px; } /* =========================== page: questions =========================== */ .questions{ margin-top: 20px; margin-bottom: 20px; list-style-type:decimal; font-size: 2rem; } .questions > strong{ display: inline-block; font-size: 1.4rem; max-width: 50em; } /* =========================== page: variables =========================== */ .uppgifts-div{ background-color: white; height: fit-content; border: 2px solid #1bbb85; border-radius: 5px; padding: 20px; box-shadow: 2px 2px 3px #1bbb85, 2px -2px 3px #1bbb85, -2px 2px 3px #1bbb85, -2px -2px 3px #1bbb85; margin-bottom: 30px; } .uppgifts-div > * { color: black; } .variabel-ul{ display: flex; flex-direction: column; justify-content: center; align-items: center; } .variabel-ul > li{ width: 250px; } .variabelP{ font-size: 1.1rem; text-align: center; } .variabelP > a:hover, .variabelP > a:focus, .backA:hover, .backA:focus{ color: black !important; font-weight: bold; font-size: 1.1rem; transition: all 0.5s ease-in-out; } /* =========================== page: iterations =========================== */ .courseList{ display: flex; flex-direction: column; } .courseList > li{ margin-left: 50px; } /* =========================== page: forms =========================== */ #form, #form2{ display: grid; grid-template-rows: 1fr 1fr; gap: 10px; } label{ display: block; font-weight: bold; margin-bottom: 10px; } input{ width: 100%; } .deleteButtons, input[type="submit"]{ background: #383b3a; color: #1bbb85; border: 2px solid #1bbb85; border-radius: 5px; font-weight: bold; } #form3{ display: flex; flex-direction: column; gap: 5px; } #todoBtn, #clearBtn{ width: fit-content; padding: 10px; } #todoButtons{ display: flex; flex-direction: row; justify-content: space-between; } #todoInput{ font-size: 1.2rem; height: 40px; border: 1px solid #1bbb85; } #todoErrorMsg{ color: red; } #send, #send2{ margin-bottom: 20px; } .todo-line{ border: 1px solid #1bbb85; border-radius: 10px; padding: 10px; margin: 5px; display: flex; flex-direction: row; justify-content: space-between; align-items: center; font-weight: bold; } .deleteButtons{ padding: 5px; } /* =========================== page: moment4 =========================== */ .blogpostChangeDeleteRows{ display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-bottom: 10px; } .login-rows{ display: flex; flex-direction: row; justify-content: left; align-items: flex-end; gap: 15px; } .login-rows2{ display: flex; flex-direction: column; justify-content: left; align-items: flex-start; gap: 15px; } #registerBtn{ display: inline-block; width: 122px; height: 55px; transition: all 0.5s ease-in-out; } #publishBtn{ display: inline-block; width: 155px; height: 55px; transition: all 0.5s ease-in-out; margin-bottom: 10px; } #loginBtn{ display: block; width: 100px; height: 55px; transition: all 0.5s ease-in-out; } #logoutBtnSection{ display: flex; flex-direction: row; justify-content: flex-end; align-items: center; gap: 10px; } #logoutBtn{ font-size: 0.7rem; width: 80px; height: 30px; transition: all 0.5s ease-in-out; } #registerBtn:hover, #registerBtn:focus, #loginBtn:hover, #loginBtn:focus{ transform:scale(1.1); font-size: 1.1rem; transition: all 0.5s ease-in-out; animation: SpecialHeight 0.5s ease-in-out; } .login-fields{ border: 1px solid #1bbb85; width: 100%; height: 55px; font-weight: bold; padding-left: 10px; padding-top: 20px; } .login-fields:hover{ box-shadow: 1px 1px 1px #1bbb85, 1px -1px 1px #1bbb85, -1px 1px 1px #1bbb85, -1px -1px 1px #1bbb85; transition: box-shadow 0.5s ease-in-out; } .login-fields:focus{ outline: none; border: 0; box-shadow: 5px 5px 5px #1bbb85, 5px -5px 5px #1bbb85, -5px 5px 5px #1bbb85, -5px -5px 5px #1bbb85; transition: box-shadow 0.5s ease-in-out; } .login-parts{ width: 100%; position: relative; } .textarea{ height: 200px; } .textarea2{ height: 50px; } .login-parts > label { position: absolute; top: 15px; left: 10px; transition: all 0.5s ease-in-out; font-size: 1.4rem; } .login-fields:focus ~ label, .login-fields:hover ~ label, .notEmpty ~ label{ position: absolute; top: 0px; left: 0px; font-size: 0.9rem; transition: all 1s ease-in-out; padding-left: 7px; padding-bottom: 2px; padding-top: 2px; padding-right: 7px; background-color: #383b3a; color: #1bbb85; box-shadow: 2px 2px 3px #1bbb85, 2px -2px 3px #1bbb85, -2px 2px 3px #1bbb85, -2px -2px 3px #1bbb85; } #specialh2, #special1{ animation: SpecialWidth 1s ease-in-out; } @keyframes SpecialWidth { 0% { transform: scaleX(0.01); } 100% { transform: scaleX(1); } } @keyframes SpecialHeight { 0% { transform: scale(1); } 100% { transform: scale(1.1); } } #secretBtn{ position: relative; cursor: pointer; border: 0; outline: 0; display: block; margin: 0 auto; background-color:rgb(16, 16, 16); font-size:13rem; color:#1bbb85; width: 169px; height: 420px; } #secretBtn:before, #secretBtn:after{ content: ''; position: absolute; top: -1px; left: -1px; background: linear-gradient(90deg,rgb(16, 16, 16),#383b3a, rgb(16, 16, 16)); background-size: 100% 100%; width: calc(100% + 6px); height: calc(100% + 6px); border-radius: 100%; z-index: -2; animation: animate 3s ease-in; } #secretBtn:after{ filter: drop-shadow(5px 5px 10px rgb(33, 89, 200)) drop-shadow(-5px -5px 10px rgb(255, 135, 29)); } #secretBtn:hover{ background-color:rgb(16, 16, 16); box-shadow: 25px 25px 25px #1bbb85, 25px -25px 25px #1bbb85, -25px 25px 25px #1bbb85, -25px -25px 25px #1bbb85; border-radius: 90%; transform: translateY(-50%) scale(2.5) rotate(1080deg); transition: box-shadow 5s ease-in-out, transform 5s ease-in-out; } /* =========================== ERRORS and SUCCESSES =========================== */ .error{ font-weight: bold; color: red; } .success{ font-weight: bold; color: #1bbb85; } /* =========================== MEDIA QUERIES =========================== */ @media only screen and (min-width: 1350px){ #nav-ul{ justify-content: center; align-content: center; } } @media only screen and (max-width: 800px){ .login-rows{ flex-direction: column; justify-content: center; align-items: flex-start; } #secretBtn:hover{ transform: translateY(-80%) scale(2) rotate(1080deg); } } @media only screen and (max-width : 600px){ #nav-ul{ justify-content: center; align-content: center; } .menu-btn{ padding: 10px; margin: 5px; width: 175px; } .blogpostChangeDeleteRows{ flex-direction: column; } h1{ font-size: 2rem; } span, i, p{ text-align: justify; } }

Dold text

include/class/Blog.class.php

<?php // CLASS for BLOG class Blog{ // Private Class Variables for "BLOG" private $db_host; // Database Hostname private $db_user; // Database User private $db_password; // Database Password private $db_database; // Database Name public $mysqli; // Connected DB Connection // Run First When New "BLOG" Object Is Created function __construct(string $db_host, string $db_user, string $db_password, string $db_database){ // Check parameters for HOSTNAME, USERNAME, PASSWORD & DATABASE NAME before Database Connection if($db_host != "" && $db_user != "" && $db_password != "" && $db_database != ""){ // Assign all 4 when no one is empty $this->db_host = $db_host; $this->db_user = $db_user; $this->db_password = $db_password; $this->db_database = $db_database; // Try Connect to Datbase on created BLOG Object $this->connectDB($this->db_host, $this->db_user,$this->db_password,$this->db_database); } } // Function to connect to Database by returning a mysqli-Object private function connectDB(string $host, $user, $password, $database){ // Try connecting try { $this->mysqli = new mysqli($host,$user,$password,$database); } // Catch error when trying to connect and output custom message catch(mysqli_sql_exception $error){ die("Sheesh! Database connection failed for some reason!"); } // If connection succeeded, return the connection in variable return $this->mysqli; } /// These Database Functions ONLY work when there is a connection to a database. /// You must specificy in own SQL Query Strings the name of Table and Column and Row when applicable // Function to check if User already exists in database public function userAlreadyExist($username) : bool{ // Prepare and send SQL Query $sql = "SELECT username FROM users WHERE username='$username'"; $result = $this->mysqli->query($sql); // If 1 row exist then chosen username already exists if($result->num_rows > 0){ return true; // So return true } else {return false;} // Otherwise false } // Function to check if email already exists in database public function emailAlreadyExist($email) : bool{ // Prepare SQL Query $sql = "SELECT email FROM users WHERE email='$email'"; $result = $this->mysqli->query($sql); // If 1 row exist then chosen email already exists if($result->num_rows > 0){ return true; // So return true } else {return false;} // Otherwise false } // Function to registering new user after valid inputs on register page public function registerUser($username, $email, $password) : bool{ // Run password_hash function on $password $hashed_password = hashPW($password); // SQL Injection Protection $username = $this->mysqli->real_escape_string($username); $email = $this->mysqli->real_escape_string($email); $password = $this->mysqli->real_escape_string($password); // Prepare SQL Query for register $sql = "INSERT INTO users(username,email,hashed_password,account_activated) VALUES('$username','$email','$hashed_password',1);"; // Try Run SQL Query for registration if($this->mysqli->query($sql)){ return true; // Return TRUE if registration succeeds } else {return false;} // Else return FALSE } // Function for logging in user using hashed password public function loginUser($username, $password) : bool { // SQL Injection Protection $username = $this->mysqli->real_escape_string($username); $password = $this->mysqli->real_escape_string($password); // Prepare SQL Query by only looking up username $sql = "SELECT * FROM users WHERE username='$username'"; // Make SQL Query $result = $this->mysqli->query($sql); // Get hashed_password column and verify password if($result->num_rows > 0){ $row = $result->fetch_assoc(); $stored_password = $row['hashed_password']; // Store session with username and return True if hashed password is a match if(verifyPass($password,$stored_password)){ $_SESSION['username'] = $username; return true;} else {return false;} } // No match means return false else{return false;} } // Function to retrieve all blog posts public function get2LatestBlogPosts($username) : array{ // Prepare SQL Query where only the 2 latest blogposts are shown // One when showing all available if($username == ""){ $sql = "SELECT * FROM blogposts ORDER BY blogpost_created DESC LIMIT 2"; } // And one when a specific users need to see them such as handlign one owns blog articles else { $sql = "SELECT * FROM blogposts WHERE blogpost_created_by='$username' ORDER BY blogpost_created DESC LIMIT 2"; } // Make SQL Query $result = mysqli_query($this->mysqli, $sql); // Return results as an ASSOCIATIVE Array return mysqli_fetch_all($result, MYSQLI_ASSOC); } // Function to retrieve all blog posts public function getAllBlogPosts($username) : array{ // Prepare SQL Query where latest blogposts are shown at the top // One when showing all available if($username == ""){ $sql = "SELECT * FROM blogposts ORDER BY blogpost_created DESC"; } // And one when a specific users need to see them such as handlign one owns blog articles else { $sql = "SELECT * FROM blogposts WHERE blogpost_created_by='$username' ORDER BY blogpost_created DESC"; } // Make SQL Query $result = mysqli_query($this->mysqli, $sql); // Return results as an ASSOCIATIVE Array return mysqli_fetch_all($result, MYSQLI_ASSOC); } // Function to retrieve blog post by ID public function getBlogPostById($BlogPostId,$username) : array{ $BlogPostId = intval($BlogPostId); // Prepare SQL Query // One where there is no username, someone just opening a blogpost if($username==""){ $sql = "SELECT * FROM blogposts WHERE id=$BlogPostId"; } // And one where a certain username is trying to access it else { $sql = "SELECT * FROM blogposts WHERE blogpost_created_by='$username' AND id=$BlogPostId"; } // Make SQL Query $result = mysqli_query($this->mysqli, $sql); // Return results as an ASSOCIATIVE Array return mysqli_fetch_all($result, MYSQLI_ASSOC); } // Function to INSERT Data to DB by taking an INSERT INTO SQL Query-STRING public function insertDB($tableName, ...$sqlColVal) : bool{ // SQL Query to build upon. We can begin by selecting // Table name to insert Columns & Values into $unpackArgList = "INSERT INTO $tableName ("; // Always equal number of columns as values when using INSERT // so let's divide two numbers to loop through them same number of times $numOfCol = count($sqlColVal)/2; $numOfVal = count($sqlColVal)/2; // Let's now loop through the Column names into INSERT variable "$unpackArgList" // Since Columns are Strings, no need to check if it is INT or STRING data type for($i=0; $i<$numOfCol; $i++){ // Column names are not provided by user so no need to protect against // SQL Injection attacks there. We are screwed if they can see this. $unpackArgList .= $sqlColVal[$i] . ","; } // Remove last comma and begin to loop through values now $unpackArgList = substr_replace($unpackArgList, ') VALUES(',-1); // Let's now loop through the Values into INSERT variable "$unpackArgList" // We must continue inside of the entire Variadic array so we begin from // the end of number of Columns and now loop through the entire count of arguments for($j=$numOfCol; $j<count($sqlColVal); $j++){ // Let's check if it is INT value or STRING value and add '' (or not) accordingly // Is number? Then add without '' if(is_numeric($sqlColVal[$j])){ // Applying real_escape_string to protect against SQL Injection Attacks $unpackArgList .= $this->mysqli->real_escape_string($sqlColVal[$j]) . ","; } // Otherwise, add with '' as we assume it is String otherwise else{ // Applying real_escape_string to protect against // SQL Injection Attacks PLUS making sure it is stored as string with '' $unpackArgList .= "'" . $this->mysqli->real_escape_string($sqlColVal[$j]) . "',"; } } // Finally, remove last comma and replace it with ) to finish off the query variable $unpackArgList = substr_replace($unpackArgList, ')',-1); // Now we TRY Send the query and we CATCH errors so // we can return false with custom error messages try { // Try Insert into Database if($this->mysqli->query($unpackArgList)){ return true; // Return TRUE if success } else { // Or throw exception and catch error but don't show that to user throw new Exception("Query failed for some reason! See error message.\n"); }} // Catch error and return false so custom error can be displayed instead catch(Exception $e){ return false; } } // Function to DELETE Data Row in DB by taking // Three parameters (user trying to delete it, name of Table and Table Row which is its id) public function deleteDBRow($username, $tableName,$tableRow) : bool{ // SQL Query with The 3 Received Arguments $deleteRow = "DELETE FROM $tableName WHERE blogpost_created_by='$username' AND id=$tableRow"; // Try Delete chosen Row by correct user in chosen Table in Database try { // Attempting to delete row in Database $this->mysqli->query($deleteRow); // Check if any rows were affected, that is, was any row actually deleted? if($this->mysqli->affected_rows > 0){ // Return true if chosen row was actually deleted return true; } // Otherwise return false if NO else{return false;}} // Catch other possible error and still return false catch(Exception $e){return false;} // Also return false if entire function failed return false; } // Function to UPDATE a single blog post which consists of the // title and text of the blog and which blog post (its id=$tableRow) public function updateBlogPost($valueTitle,$valueText,$tableRow) : bool{ // Protect against SQL Injections $valueTitle = $this->mysqli->real_escape_string($valueTitle); $valueText = $this->mysqli->real_escape_string($valueText); // SQL Query with The 4 Received Arguments $updateRow = "UPDATE blogposts SET blogpost_title='$valueTitle', blogpost_text='$valueText' WHERE id=$tableRow"; // Did it manage to UPDATE Data Row in Selected Table in DB? try { // Try UPDATE Database if($this->mysqli->query($updateRow)){ return true; // Return TRUE if success } else { // Or throw exception and catch error but don't show that to user throw new Exception("Query failed for some reason! See error message.\n"); }} // Catch error and return false so custom error can be displayed instead catch(Exception $e){ return false; } // Also return false if entire function failed return false; } // Function to DB Connection public function closeDB(){ $this->mysqli->close(); } // Also close DB Connection when Class Object is destroyed itself function __destruct(){ $this->mysqli->close(); } } ?>

Dold text

(Del 2 kommer, kanske del 3 också!)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Källkod till PHP Moment 4 & Labb 3 i Databaser troligen godkänd! [Del 2 av 4]

[DEL 2 AV 4]: Källkod till PHP Moment 4 (skapa mapparna "css", "include" och "class" inuti "include" och kör sedan "install.php" och sedan "index.php"):
include/class/Todo.class.php

<?php // Klassnamn class Todo { // Privata variabler som bara nås via klassens metoder private $todoInput; // Inmatningsfält för Att göra private $JSONFile; // Namn på JSON-fil som ska hanteras public $todos = array(); // Alla Att göra samlade i en Array // Klassmetoder // Konstruktör - körs när ny klassinstans initierats i övrig php-kod function __construct(string $JSONFile){ // Tilldela JSON-filnamn $this->JSONFile = $JSONFile; // Kontrollera om tilldelat JSON-filnamn finns if(file_exists($JSONFile)){ // Läs in JSON-fil om den finns $file = file_get_contents($JSONFile); // Omvandla inläst JSON-fil till array och tilldela i $todos $this->todos = json_decode($file, true); } // Annars skapa JSON-fil med en tom array och tilldela $todos det else{ // Skapar filnamnet om det ej redan finns och skriver [] till filen $file = file_put_contents($JSONFile, '[]'); // Tilldelar $todos värdet av en tom array $this->todos = json_decode($file, true); } } // setTodo - tar, kontrollerar & tilldelar värde från inmatningsfält ("Sak att göra...") function setTodo(string $todoInput) : bool { // Om inmatningslängd är minst 5 tecken, dvs., fler än 4 tecken if(strlen($todoInput) > 4){ // Tilldela inmatningsvärde $this->todoInput = $todoInput; return true; // Och returnera sant, dvs., kvitto på att det gick } return false; // Annars returnera falskt, dvs., kvitto på att det INTE gick } // getTodos - returnerar JSON-arrayen med Att göra-listan function getTodos() { // Läs in JSON-fil $file = file_get_contents($this->JSONFile); // Spara JSON-fil som JSON-Array i $todos-array-variabeln $this->todos = json_decode($file, true); // Returnera $todos-arrayen return $this->todos; } // removeTodo - tar bort en vald Todo via "Klar"-knappen function removeTodo(int $arrayIndex){ // Läs in JSON-fil och omvandla till JSON-Array $file = file_get_contents($this->JSONFile); $jsonArray = json_decode($file, true); // Ta bort vald aktivitet ur arrayen tack vare dess mottagna indexvärde unset($jsonArray[$arrayIndex]); // Spara tillbaka som en JSON-fil igen file_put_contents($this->JSONFile, json_encode($jsonArray, JSON_PRETTY_PRINT)); } // clearTodos - rensar hela JSON-arrayen function clearTodos(){ // Rensar JSON-filen genom att bara skriva in tom array på nytt i den vilket då överskriver allt annat file_put_contents($this->JSONFile,'[]'); } // addTodo - lägger till Att göra i JSON-arrayen function addTodo(string $addtodo){ // Öppna JSON-fil $JF = file_get_contents($this->JSONFile); // Lagra den i tillfällig variabel $tempArray = json_decode($JF,true); // Lägg till sträng som array-element i den tillfälliga variabeln array_push($tempArray, htmlspecialchars($addtodo)); // Spara den nya arrayen i JSON-filformat(via json_encode) igen $JFData = json_encode($tempArray); file_put_contents($this->JSONFile, $JFData); } } ?>

Dold text

include/class/Validate.class.php

<?php // Class Validate contains simple check functions to speed up Flow of Control in regular PHP code // It could almost be considered a "Library of Check Functions" // They are ALL static so they can be used Validate::FunctionName // and without initiating an instance of the object first class Validate{ // Check if field is empty (mainly used by input fields) // This converts numbers to a string also first public static function isEmpty($field) : bool{ // Return True if field is empty, trim removes whitespace at start and end of string if(strval(trim($field)) == ""){ return true; } // Otherwise return False return false; } // Check if two fields are same (mainly used by repeated input fields) // This converts numbers to a strings also first public static function isTwoSame($field1,$field2) : bool{ // Return True if both fields are same if(strval($field1) == strval($field2)){ return true; } // Otherwise return False return false; } // Check if string (even if it is only numbers) is at least of length $length // This converts numbers to a string and then reads its length as string public static function isLeastLength($string, $length) : bool{ // Trim() removes whitespace at start and end of string // Return True if $string is at least $length long // So, longer than $length also returns True if(strval(strlen(trim($string))) >= intval($length)){ return true; } // Otherwise return False return false; } // Check if string (even if it is only numbers) is within allowed max length of $length // This converts numbers to a string and then reads its length as a string public static function isMaxAllowedLength($string, $maxlength) : bool{ // Trim() removes whitespace at start and end of string // Return True if $string is at most $maxlength long // So, lower than $maxlength also returns True if(strval(strlen(trim($string))) <= intval($maxlength)){ return true; } // Otherwise return False return false; } // Check if input is valid email by inbuilt filter_var in PHP // Converts field to string first so numbers cannot fool it public static function isEmail($string) : bool { // If $string is a valid email... if(filter_var(strval($string), FILTER_VALIDATE_EMAIL)){ return true; // ...return True } // Otherwise false return false; } // Trim() removes whitespace at start and end of string // Function to check against what a valid password is. // It checks length, if uppercase & lowercases are used and certain numbers // RETURNING TRUE = Valid password | RETURNING FALSE = Invalid password public static function validPassword($password) : bool { $password = strval($password); // Check first so password is within 12-24 character range if(strlen(trim($password)) > 11 && strlen(trim($password)) < 25){ // Check password requirements with regex below // It says "At least 12 characters, A-Z both uppercase and lowercase are allowed // At least 1 uppercase & lowercase, at least one number, at least 1 special character" $password_requirements = "/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[_?!-]).{12,}$/"; // If $password conforms to regex if(preg_match($password_requirements,$password) == 1) { return true; // return true = chosen password is Valid } else {return false;} // otherwise false } else {return false;} // and also otherwise false if length not correct } // Function to validate username (between 6 and 21 letters long) public static function validUsernameLength($username) : bool { // Username not empty and between 6 and 21 characters long if(strlen(trim($username)) > 5 && strlen(trim($username)) < 22){ return true; } // Otherwise return False else {return false;} } // Function to validate username type (only a-z letters, at least 6 letters) public static function validUsernameType($username) : bool { // Requirements: At least 6 characters and only small letters of a-z $username_requirements = "/^(?=.*?[a-z])[a-z]{6,}$/"; // If username requirements fulfilled if(preg_match($username_requirements,$username) == 1){ return true; // Return True } else {return false;} // Else Return False } // END OF CLASS } ?>

Dold text

include/c-footer.php

</main> <footer> <span id="footer-text">© 2023 WebbKodsLärlingen<br> VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans) - Moment 2, 3 & 4</span> </footer> </div> </body> </html>

Dold text

include/c-header.php

</head> <body> <div class="main-layout"> <div> <header> <h1>FRAMTIDSBLOGGEN I PHP MOMENT 2,3 & 4</h1> </header>

Dold text

include/c-nav.php

<nav> <ul id="nav-ul"> <li class="menu-btn <?php echo setCurrentBtn(0); ?>">0. <a href="index.php"> Startsida </a> </li> <li class="menu-btn <?php echo setCurrentBtn(1); ?>">1. <a href="p-variables.php"> Variabler </a> </li> <li class="menu-btn <?php echo setCurrentBtn(2); ?>">2. <a href="p-conditions.php"> Villkor </a> </li> <li class="menu-btn <?php echo setCurrentBtn(3); ?>">3. <a href="p-iterations.php"> Upprepningar </a> </li> <li class="menu-btn <?php echo setCurrentBtn(4); ?>">4. <a href="p-forms.php"> Formulär </a> </li> <li class="menu-btn <?php echo setCurrentBtn(5); ?>">5. <a href="p-fileread.php"> Filinläsning </a> </li> <li class="menu-btn <?php echo setCurrentBtn(6); ?>">6. <a href="p-moment3.php"> Moment 3 </a> </li> <li class="menu-btn <?php echo setCurrentBtn(7); ?>">7. <a href="p-moment4.php"> Moment 4 </a> </li> </ul> </nav> </div> <main>

Dold text

include/config.php

<?php // PHP Configuration code before any HTML is loaded // Function adding CSS class to mark current page in Navigation menu function setCurrentBtn($btnNumber){ return $GLOBALS['currentBtn']==$btnNumber ? 'current-btn' : ''; } // Same as above but for menu items after logged in function setCurrentBtnAdmin($btnNumberAdmin){ return $GLOBALS['currentBtnAdmin']==$btnNumberAdmin ? 'current-btn-admin' : ''; } // Function outputting received string in Title element function pageTitle($pTitle){ return $pTitle . " | FRAMTIDSBLOGGEN i PHP Moment 2, 2 & 4"; } // Function linking to next solved assignment (only Moment 3) function nextPage($nPage, $pName){ return "<p style='text-align: center;' class='variabelP'><a href='$nPage.php'>Nästa uppgift ($pName)</a></p>"; } // Function that returns error paragraph message in red (use .error class in CSS) // $center = false means its default value is false when it is not given as argument function showError($msg, $center = false){ if($center==true){ // Align text center if $center is true/1 return "<p class='error' style='text-align:center;'>$msg</p>"; } else { // Else just return without aligning text in center when false as default return "<p class='error'>$msg</p>"; } } // Function that outputs session-based error message and then unsets it function showSessionError($session,$errormsg){ if(isset($_SESSION[$session])){ // If session error exists echo showError($errormsg); // show it unset($_SESSION[$session]); // and unset it } } // Same as above but for session-based successes function showSessionSuccess($session,$errormsg){ if(isset($_SESSION[$session])){ // If session success exists echo showSuccess($errormsg); // show it unset($_SESSION[$session]); // and unset it } } // Function that returns shortened version of typical "Is this variable inside of $_POST declared?" function clickedP($name){ return isset($_POST[$name]); } // Same as above but for $_GET function clickedG($name){ return isset($_GET[$name]); } // Function that returns value inside of a $_POST to speed up repeated things // Same function as above but more semantically meaningful in terms of name function setPost($string){ return htmlspecialchars($_POST[$string],ENT_QUOTES, 'utf-8'); } // Same as above but for $_GET function setGet($string){ return htmlspecialchars($_GET[$string],ENT_QUOTES, 'utf-8'); } // Function to output previous field value in input fields ($_POST) function previousFieldvalueP($btn, $value){ if(clickedP($btn)){ if(setPost(htmlspecialchars($value,ENT_QUOTES, 'utf-8')) != ""){ return setPost(htmlspecialchars($value,ENT_QUOTES, 'utf-8')); } else return ""; } } // Function to output previous field value in input fields ($_GET) function previousFieldvalueG($btn, $value){ if(clickedG($btn)){ if(setG(htmlspecialchars($value,ENT_QUOTES, 'utf-8')) != ""){ return setG(htmlspecialchars($value,ENT_QUOTES, 'utf-8')); } else return ""; } } // Function that returns error message when an input field has not been filled out after clicking submit button function fillOutEmptyFieldP($btn,$field,$msg){ if(clickedP($btn)){ if(Validate::isEmpty($_POST[$field])){ echo showError($msg);}} } // Same as above but for $_GET function fillOutEmptyFieldG($btn,$field,$msg){ if(clickedG($btn)){ if(Validate::isEmpty($_GET[$field])){ echo showError($msg);}} } // Function that returns success paragraph message in green (use .success class in CSS) function showSuccess($msg){ return "<p class='success'>$msg</p>"; } // Function returns hashed password function, shortened function hashPW($pass){ return password_hash($pass, PASSWORD_DEFAULT); } // Function returns verified password function, shortened function verifyPass($verify,$hashed){ return password_verify($verify, $hashed); } // Function checking if user is logged in function isLoggedIn(){ // Logged in since before? if(isset($_SESSION['username'])){ return true; // Return True } else{ // Otherwise Return False return false; } } // Function returning in the style of YYYY:MM:DD klockan HH:MM function blogPublishDate($date){ // Concatenate "YYYY:MM:DD klockan HH:MM av " $newDate = substr($date, 0,-9) . " klockan " . substr($date,10,-3) . " av "; // and return that as a string return strval($newDate); } // Same as above but only in the style of YYYY:MM:DD (used when showing Current Blogposts by User) function blogPublishDateShort($date){ // Concatenate "YYYY:MM:DD klockan HH:MM av " $newDate = substr($date, 0,-9); // and return that as a string return strval($newDate); } // Function that redirects user to login page. It is // only applicable to pages where you must be logged in. function checkLoginOrRedirect(){ // Check if not logged in if(!isLoggedIn()){ // If not logged in set redirected session $_SESSION['redirected'] = true; // and redirect to login page header("Location: p-moment4-login.php"); } // If user is logged in, code above will be ignored. } // Function to automatically include class php file when used spl_autoload_register(function ($class_name) { include 'class/' . $class_name . '.class.php'; //path to classes }); // Developer Mode with local DB Connection $devmode = true; if($devmode){ // Function for reporting errors in browsers error_reporting(-1); ini_set('display_errors', 1); // Database Variables | DEFAULT FOR XAMPP for local Database Connections define("DB_HOSTNAME","localhost"); // Address to Database hostname define("DB_USERNAME","root"); // Username to log into database define("DB_DATABASE","moment4_WKL"); // Database name to use after login define("DB_PASSWORD","localhost"); // Password to log into database } // If not in Developer Mode, use these public DB Connection Settings else{ define("DB_HOSTNAME","?"); // Address to Database hostname define("DB_USERNAME","?"); // Username to log into database define("DB_DATABASE","?"); // Database name to use after login define("DB_PASSWORD","?"); // Password to log into database } // Initiate Database Connection and Start new OR continue current PHP Session // Check __construct() in Blog.class.php for error handling when connecting $DB = new Blog(DB_HOSTNAME,DB_USERNAME,DB_PASSWORD,DB_DATABASE); session_start(); // session_start() must run before any HTML code is loaded into existence // First initializing PHP Code ends here and first HTML content is now loaded ?> <!DOCTYPE html> <html lang="sv"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/styles.css"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;80..." rel="stylesheet"> <script> // JS EventListener to toggle a CSS class. This is so PHP code can validate // Otherwise if I use input attribute required in text fields, PHP code would // not be the one to output demands or warnings which is required by this course. // Wait for entire HTML to be loaded first. document.addEventListener('DOMContentLoaded', function() { // Then select all elements with class name fieldListen let inputFields = document.querySelectorAll('.fieldListen'); // Only add EventListeners for such existing input fields. if (inputFields.length > 0) { for (let i = 0; i < inputFields.length; i++) { // Add class to existing fields with already existing values inside of them if(inputFields[i].value !== ''){inputFields[i].classList.add('notEmpty');} // Add event listener for when focus is lost inputFields[i].addEventListener('blur', function() { // Toggle between CSS class depending on field is empty or not if (this.value !== ''){this.classList.add('notEmpty'); }else {this.classList.remove('notEmpty');}});}}}); </script>

Dold text

include/secret.php

<button id="secretBtn" title="( ͡° ͜ʖ ͡°) ??? HEMLIG ÖVERRASKNING ??? ( ͡° ͜ʖ ͡°)">?</button>

Dold text

courses.txt

Webbutveckling I Introduktion till programmering med JavaScript Digital bildbehandling för webb Webbanvändbarhet Webbutveckling II Databaser Webbdesign för CMS Webbutveckling III

Dold text

index.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=0; ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <?php include("p-questions.php");?> <?php include("include/secret.php")?> <?php include("include/c-footer.php");?>

Dold text

install.php

<?php include("include/config.php"); // PHP Code to "install" Blog locally // "Reset" tables by dropping them if they exist echo "<br>OM TABELL MISSLYCKAS INSTALLERAS:<br>--------------------------------------------- <br>/!\- Skapa först en databas som heter moment4_WKL och kör skriptet igen!<br> <br>/!\- Se till att databasanslutningen är korrekt konfigurerad (se under \$devmode i config.php för mer): <br>****** \$devmode = true; <br>****** Host: localhost <br>****** Username: root <br>****** Database: moment4_WKL <br>****** Password: localhost<br><br><br>...RADERAR GAMLA OCH SKAPAR NYA TABELLER...<br><br>"; $sql = "USE moment4_WKL;"; // Select database $sql .= "SET foreign_key_checks = 0;"; // Disable foreign key checks so tables can be deleted $sql .= "DROP TABLE IF EXISTS blogposts;"; // Table for blog posts $sql .= "DROP TABLE IF EXISTS users;"; // Table for users // Create the actual tables after "resetting" them // Users = to register, and login with $sql .="CREATE TABLE users( username VARCHAR(21) PRIMARY KEY, email VARCHAR(128) NOT NULL UNIQUE, hashed_password VARCHAR(256) NOT NULL, account_activated INT (1) NOT NULL DEFAULT 0, registered_date timestamp NOT NULL DEFAULT current_timestamp);"; // Blogposts = to post blog posts with users $sql .= "CREATE TABLE blogposts( id INT(11) PRIMARY KEY AUTO_INCREMENT, blogpost_title VARCHAR(64) NOT NULL, blogpost_text TEXT NOT NULL, blogpost_created timestamp NOT NULL DEFAULT current_timestamp, blogpost_created_by VARCHAR(21), FOREIGN KEY (blogpost_created_by) REFERENCES users(username));"; // Enable foreign key checks again after created all tables $sql .= "SET foreign_key_checks = 1;"; echo "<pre>$sql</pre>"; if($DB->mysqli->multi_query($sql)){ echo "<br>TABELLEN LYCKADES INSTALLERAS!<br>MYCKET NÖJE! :-)<br>"; }else {echo "<br>Tabellerna misslyckades installeras!<br>Skapa först en databas som heter \"moment4_WKL\" och prova igen!<br>Eller ändra i config.php på raderna 165-176 efter egen lokal databaskonfigurering!";} ?>

Dold text

p-calculate.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=4; ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <div class="uppgifts-div"> <h3>Beräkna arean</h3> <?php // Kolla att Skicka knapp klickats if(isset($_POST['skicka2'])){ // Kolla att BÅDA fält matats in if(!empty($_POST['langd']) && !empty($_POST['bredd'])){ $langd = $_POST['langd']; $bredd = $_POST['langd']; echo "Längden $langd meter och bredden $bredd meter ger en area på " . $langd*$bredd . " kvadratmeter."; } // Annars skriv ut uppmaning om att mata in båda fält. else{ echo "<span style='color: red; font-size:1rem;'>Både längd och bredd måste anges!</span>"; } echo "<br><br><a href='p-forms.php' class='backA' style='display:block; text-align:center;'>Gå tillbaks till föregående sida</a>"; } ?> </div> <?php include("include/c-footer.php");?>

Dold text

p-conditions.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=2; ?> <title><?= pageTitle("Villkor"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 2. Villkor</h2> <div class="uppgifts-div"> <h3>Datum/klockslag: ÅÅÅÅ-MM-DD:TT.MM</h3> <?php $dateNow = date('Y-m-d') . ':' . date('H.i'); echo "<p style='text-align: center;'>Datum/klockslag: $dateNow<br><br></p>"; ?> <h3>Idag är det (inte) söndag</h3> <p style='text-align: center;'>Idag är det <?php $isSunday = date('D'); echo $isSunday === 'Sun' ? ' söndag' : ' inte söndag'; ?>.<br><br></p> <h3>Det är morgon/förmiddag/eftermiddag eller kväll/natt</h3> <p style='text-align: center;'>Det är <?php // Gammalt variabelnamn från JS-Intro-kursen - gammal favorit! $getHM = date('H:i'); if($getHM >= '06:00' && $getHM <= '08:59'){ echo 'morgon'; } else if($getHM >= "09:00" && $getHM <= '11:59'){ echo 'förmiddag'; } else if($getHM >= '12:00' && $getHM <= '17:59'){ echo 'eftermiddag'; } else if($getHM >= '18:00' && $getHM <= '23:59' || ($getHM >= '00:00' && $getHM <= '05:59')){ echo 'kväll/natt'; } ?>.</p> <h3>Idag är det <i>Veckodag</i></h3> <p style='text-align: center;'>Idag är det <?php $weekDay = date('D'); switch($weekDay){ case 'Mon': echo 'måndag'; break; case 'Tue': echo 'tisdag'; break; case 'Wed': echo 'onsdag'; break; case 'Thu': echo 'torsdag'; break; case 'Fri': echo 'fredag'; break; case 'Sat': echo 'lördag'; break; case 'Sun': echo 'söndag'; break; }?>.</p><br> <?php echo nextPage('p-iterations','3. Upprepningar');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-fileread.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=5; ?> <title><?= pageTitle("5. Filinläsning"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 5. Filinläsning</h2> <div class="uppgifts-div"> <h3>Inläsning av extern textfil</h3> <?php // Om filen ej existerar. if(!file_exists('courses.txt')){ echo "Filen kunde inte hittas!"; } // Öppnna annars för inläsning ('r') else{ echo "Filen finns. Öppnar den nu och skriver ut som en punktlista...<br><br>"; $fp = fopen('courses.txt','r'); // Skriver ut <ul>-element echo "<ul style='margin-left: 30px;'>"; // feof = slutet på filen så betyder "så länge INTE // slutet på filen har nåtts för öppnad fil $fp så..." while(!feof($fp)){ echo "<li>" . fgets($fp) . "</li>"; } // Avsluta punktlistan, skapa luft nedanför innan nästa länk echo "</ul><br>"; }?> <?php echo nextPage('index','0. Startsida/Frågor');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-forms.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=4; ?> <title><?= pageTitle("4. Formulär"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 4. Formulär</h2> <div class="uppgifts-div"> <h3>Del 1 - Skicka data med GET</h3> <?php // Kolla att Skicka knapp klickats if(isset($_GET['skicka1'])){ // Kolla att BÅDA fält matats in if(!empty($_GET['fname']) && !empty($_GET['ename'])){ $fornamn = $_GET['fname']; $efternamn = $_GET['ename']; // Skriv ut för -& efternamn echo "Hej " . htmlspecialchars($fornamn) . " " . htmlspecialchars($efternamn) . "!"; } // Annars skriv ut uppmaning om att mata in båda fält. else{ echo "<span style='color: red; font-size:1rem;'>Du måste ange både för- och efternamn!</span>"; } } ?> <form id="form" action="p-forms.php" method="GET"> <div> <label for="fnamn">Förnamn:</label> <input id="fnamn" type="text" name="fname"> </div> <div> <label for="enamn">Efternamn:</label> <input id="enamn" type="text" name="ename"> </div> <input type="submit" name="skicka1" value="Skicka" id="send"> </form> <h3>Del 2 - Skicka data med POST</h3> <form id="form2" action="p-calculate.php" method="POST"> <div> <p style="font-size: 1rem; margin-bottom: 5px;">Beräkna arean på en yta genom att ange längd och bredd.</p> <label for="langd">Längd:</label> <input id="langd" type="number" name="langd"> </div> <div> <label for="bredd">Bredd:</label> <input id="bredd" type="number" name="bredd"> </div> <input type="submit" value="Skicka" name="skicka2" id="send2"> </form> <?php echo nextPage('p-fileread','5. Filinläsning');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-iterations.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=3; ?> <title><?= pageTitle("3. Upprepningar"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 3. Upprepningar</h2> <div class="uppgifts-div"> <h3>Del 1</h3> <p style='text-align: center;'> <?php for($i = 10; $i>0; $i--){ echo $i . '<br>'; } ?> </p><br> <h3>Del 2</h3> <p style="margin-bottom: 10px; text-align: center;">Kurslistan i den ordning kurserna ges:</p> <?php $arrCourses = array( "Webbutveckling I","Introduktion till programmering med JavaScript", "Grafisk teknik för webb för webb","Webbanvändbarhet","Webbutveckling II","Databaser", "Webbdesign för CMS","Webbutveckling III" ); // Skriv ut echo '<ul class="courseList">'; foreach($arrCourses as $arrCourse){ echo "<li>$arrCourse</li>"; } echo '</ul>'; ?><br> <h3>Del 3</h3> <p style="margin-bottom: 10px; text-align: center;">Kurslistan i bokstavsordning:</p> <?php // Sortera och skriv ut sort($arrCourses); echo '<ul class="courseList">'; foreach($arrCourses as $arrCourse){ echo "<li>$arrCourse</li>"; } echo '</ul>'; ?><br> <?php echo nextPage('p-forms','4. Formulär');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-moment3.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=6; // Initierar klass "Todo" (se även tillagd autoinkludering i config.php) och dess konstruktör med: // 1 textsträng "todolist.json", alltså filnamnet för JSON-filen ifråga $letsTodo = new Todo('todolist.json'); ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 3 - Att göra lista med PHP</h2> <div class="uppgifts-div"> <h3>En "Att göra"-lista med objektorienterad PHP</h3> <form id="form3" action="p-moment3.php" method="POST"> <label for="todoInput">Ange och lägg till ny sak att göra:</label> <input id="todoInput" type="text" name="todoItem" placeholder="Sak att göra..."> <?php // PHP-KOD som körs mellan inmatningsfält och "Lägg till"-knappen // Klickat på "Lägg till" ? if(isset($_POST['addTodo'])){ // Är det sant att minst 5 tecken har matats in? if($letsTodo->setTodo($_POST['todoItem'])){ // Lagra då "Att göra"-strängen med hjälp av klassmetod addTodo i klassen $letsTodo->addTodo($_POST['todoItem']); } // Om färre än 5 tecken, skriv ut felmeddelande else{ echo "<p id='todoErrorMsg'>Du måste ange minst fem tecken för att lägga till i listan nedan.</p>"; } } ?> <div id="todoButtons"> <input type="submit" name="addTodo" value="Lägg till" id="todoBtn"> <input type="submit" name="clearTodos" value="Rensa" id="clearBtn"> </div> </form> <div class="todoList"> <h3>Saker att göra</h3> <?php // PHP-KOD som körs nedanför inmatningsfältet och knapparna, här visas "Saker att göra"-listan $key_plus_one = 0; // Används för att numrera aktiviteterna innan de skrivs ut // Kontrollera om "Rensa" klickats på och anropa då klassmetod som tömmer hela arrayen innan den skrivs ut if(isset($_POST['clearTodos'])){ $letsTodo->clearTodos(); $key_plus_one = 0; // Allt togs bort så nollställ index } // Kontrollera om någon "Klar" klickats på if(isset($_POST['delete'])){ // Skicka med arrayIndex (omvandlat till heltal) för // vald aktivitet & ta bort med removeTodo()-klassmetoden $letsTodo->removeTodo(intval($_POST['delete'])); $key_plus_one = 0; // Något togs bort så nollställ index } // Här skrivs Att göra-listan ut och klickad "Klar" via GET tas bort. // Anropa getTodos() som returnar en array från JSON-filen // DETTA ÄR DET SISTA SOM SKA SKE PÅ SIDAN EFTERSOM DEN SKRIVER UT DET SLUTGILTIGA SOM FINNS! $Alltodos = $letsTodo->getTodos(); // Skriv ut alla "Saker att göra" från erhållna arrayen foreach ($Alltodos as $key=>$todo){ $key_plus_one += 1; // Med hjälp av $key så kan varje Klar-knapp tilldelas unikt ID när 'POST'-metoden används // som då också är säkrare än 'GET'. echo "<div class='todo-line'>$key_plus_one. " . htmlspecialchars($todo) . " <form action='p-moment3.php' method='POST'> <input type='hidden' value=$key name=delete><button class='deleteButtons'>Klar</button></form></div>"; } ?> <a class="backA" style="display: block; text-align: center;" href="todolist.json">Länk till JSON-fil</a> </div> </div> <?php include("include/c-footer.php");?>

Dold text

p-questions.php

<h2>Moment 2 - Frågor</h2> <ul> <li class="questions"> <strong>Har du tidigare erfarenhet av utveckling med PHP?</strong> <br><p>Nej, inte i denna utsträckning. Jag har för länge sedan kikat på det men förvirrats av dess sätt att användas på som exempelvis att skriva den direkt inuti HTML-kod. För säkert 10 år sedan öppnade jag upp diverse Wordpress-php-filer och förstod så klart ingenting.<br><br>Det blir roligt att öppna om samma Wordpress-filer efter CMS-kursen som då fokuserar på just Wordpress om jag minns rätt</p> </li> <li class="questions"> <strong>Beskriv kortfattat vad du upplever är fördelarna med att använda PHP för att skapa webbplatser.</strong> <br><p>Två starka fördelar:<br><br>1) Den första är att kunna skapa mer dynamiska webbplatser snabbare tack vare det modulära tänket vilket jag älskar. Jag hoppas vi aldrig går tillbaka till "vanilla HTML" på det viset utan får hålla på med både modulär php, javascript, och så vidare. C# är ju objektorienterat och OOP anammar ju delvis det där modulära tänket som jag rent av älskar!<br><br>2) Den andra stora fördelen är att kunna skapa säkra webbplatser tack vare att php-koden döljs undan. Jag upptäckte även att kommentarer inuti php döljs men om man skriver HTML-kommentarer precis utanför php-koden så syns de slags kommentarerna.</p> </li> <li class="questions"> <strong>Hur har du valt att strukturera upp dina filer och kataloger?</strong> <br><p>Jag har <i>index.php</i> i "webbserverns" rotkatalog. I mappen <i>include</i> så finns filer med prefix i filnamnen för att lättare veta vad som syftar på vad: "c" = component/komponent till en viss sida, "f" = funktion till en viss sida.<br><br>Notera således att funktionerna i <i>config.php</i> är medvetet globala då den filen ändå inkluderas på alla sidor. En första tanke var dock katalogerna <i>page</i> för sidorna (det vill säga, "p" = page/sida) och <i>function</i> för funktionerna.<br><br>Men det skippade jag då jag har haft problem med att länka rätt mellan filer som ligger i olika nivåer i olika kataloger än så länge.</p> </li> <li class="questions"> <strong>Har du följt guiden, eller skapat på egen hand?</strong> <br><p>Jag har delvis följt guiden för den grundläggande strukturen sedd i <i>index.php</i>. Då var det bara att kopiera och klistra in för alla undersidor och inkludera nödvändig php-kod där för att lösa respektive uppgift.</p> </li> <li class="questions"> <strong>Har du gjort några förbättringar eller vidareutvecklingar av guiden (om du följt denna)?</strong> <br><p>Vad som kan tolkas som förbättringar jag har gjort är i form av funktioner som kan anropas. <br><br>Exempelvis så sätts ett värde för vilken sida man är på vilket sedan anropas i <i>c-header.php</i> för att markera vilken nuvarande sida är. Detta syns <i>$currentBtn</i>-variabeln i början av undersidorna. <br><br>Annan funktion är den som anropas inuti varje <i>title</i>-element för att skriva ut undersidans namn efter webbplatsens namn. <br><br>En ytterligare funktion är den som anropas i slutet på varje uppgift: <i>nextPage</i> som returnar länknamn (ej filändelse) och strängen inom parentesen. Medför snabbare navigering. <br><br>Funktionerna finns i <i>config.php</i> då de används på varje webbsida, exklusive <i>nextPage</i> på denna sida.</p> </li> <li class="questions"> <strong>Vilken utvecklingsmiljö har du använt för att genomföra uppgiften (editor, webbserver-paket (Xampp, Lamp, Wamp eller liknande)?</strong> <br><p>Jag har använt mig av VSCode som huvudsaklig utvecklingsmiljö. Jag har använt mig av XAMPP som webbserver för PHP.<br><br>Installerade även <i>Apache</i> direkt som Service i Windows 10 för att slippa behöva starta XAMPP manuellt varje gång jag startar datorn.</p> </li> <li class="questions"> <strong>Har något varit svårt med denna uppgift?</strong> <br><p>Ja, CSS som vanligt. En sak jag inte förstod var varför jag fick felmeddelande när jag valde att inkludera typsnitt lokalt, alltså med <i>@font-face</i>.<br><br>Då stod det i <i>Console</i> att <i>"download failed font"</i> (trots att jag såg att sökvägen till filen var rätt med kataloger och filnamn).<br><br>Så jag fick använda direktlänk till Google Fonts för det futuristiska typsnittet på denna webbplats. Jag vet inte om jag hade valt fel slags varianter av typsnitten (det var .tff) eller om det är något php och typsnitt jag inte begriper mig ännu på.</p> </li> </ul>

Dold text

p-variables.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=1; ?> <title><?= pageTitle("1. Variabler"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 1. Variabler</h2> <div class="uppgifts-div"> <h3>Enskilda</h3> <?php // Skapar variabler $name = "WKL"; $age = "WKL"; $email = "WKL@WKL.se"; echo "<ul class='variabel-ul'><li>$name</li><li>$age</li><li>$email</li></ul><br>"; ?> <h3>Kombinerade</h3> <?php echo "<p class='variabelP'>Hej! Jag heter $name" . ", är " . $age . " år gammal och nås på följande e-post: " . "<a href='mailto:$email'>$email</a>.</p><br>"; echo nextPage('p-conditions','2. Villkor'); ?> </div> <?php include("include/c-footer.php");?>

Dold text

todolist.json

{ "1": "L\u00e4gga till JSON-fil l\u00e4nk" }

Dold text

Bild på Labb 3 med 3NF ER-Diagram

På återseende!

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Källkod till PHP Moment 4 & Labb 3 i Databaser troligen godkänd! [Del 3 av 4]

[DEL 3 AV 4]: Källkod till PHP Moment 4 (skapa mapparna "css", "include" och "class" inuti "include" och kör sedan "install.php" och sedan "index.php"):

p-moment4-edit-blogpost.php

<?php include("include/config.php");?> <?php $currentBtnAdmin=3; $currentBtn=7; // Mark current page in <nav> (see c-nav.php) // Redirect to login page if not logged in and set Session error message to be shown there checkLoginOrRedirect(); // Clicked on a "Ändra inlägg" link with an editid value? or just wrote it manually in address field? $postExist=false; // Prepare postExist boolean that controls echo outputs for input fields and which blog is being edited if(!clickedP('post-blogpost')){ if(clickedG('editid')){ // Convert to an integer $id = intval($_GET['editid']); // Try reading in chosen blogpost from database from currently logged in user $editPost = $DB->getBlogPostById($id,$_SESSION['username']); // If blogpost does not exist (due to access denied or wrong/invalid // 'editid' value), it will return zero rows so check for that first if(count($editPost) == 0){ // Prepare error message to show on same page, define postExist as false and unset 'editid' $_SESSION['edit-post-fail'] = 1; $postExist = false; } // If blogpost does exist, then the user has access to it also so store values from it and output else { $postExist = true; // set postExist to true helping if statements to dynamically output } } } // Actually clicked on "Ändra inlägg" button? if(clickedP('post-blogpost')){ // Then check at least and at most lengths for the two fields if( strlen($_POST['newBlogTitle']) != 0 // Not zero length && strlen($_POST['newBlogText']) != 0 // Not zero length && Validate::isLeastLength($_POST['newBlogTitle'],15) // At least 15 characters && Validate::isMaxAllowedLength($_POST['newBlogTitle'],64) // At most 64 characters && Validate::isMaxAllowedLength($_POST['newBlogText'],4000) // At most 4000 characters ) // If all OK, update in database and prepare success message { // Store variables for easier use $title = $_POST['newBlogTitle']; // Blog title $text = $_POST['newBlogText']; // Blog text $id = $_GET['editid']; // Blog id // Try update blogpost in database if($DB->updateBlogPost($title,$text,$id)) { // Prepare success message if success $_SESSION['edit-post-success'] = 1; // Also get the updated data to dynamically replace text fields and heading "Ändra blogginlägg" $editPost = $DB->getBlogPostById($id,$_SESSION['username']); $postExist = true; // Thus that the post exists is true } // Or failure message if fail else {$_SESSION['edit-post-fail2'] =1;} } } ?> <title><?php if(isLoggedIn()){ echo pageTitle("Moment 4 - " . strval($_SESSION['username']) . " inloggad"); }else { echo pageTitle("Moment 4 - Ej inloggad");}?> </title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2 id="specialh2">Moment 4 - Dataanslutningar med PHP | <?php echo isLoggedIn() ? strval($_SESSION['username']) . " inloggad" : " Ej inloggad" ?></h2> <div class="uppgifts-div" id="special1"> <form action="p-moment4-logout.php" method="POST" id="logoutBtnSection"> <?php // Show failure when a blog post failed to load (cause access denied or does not exist) or failed to update showSessionError('edit-post-fail','Blogginlägg saknas eller du saknar tillstånd att ändra!'); // Show failure when failed to update for other reason than empty fields or access denied showSessionError('edit-post-fail2','Blogginlägget lyckades ej att uppdateras!'); // Show failure message when trying to logout without using log out button showSessionError('logOutWithoutBtn','Logga ut med Logga ut-knappen!'); ?> <span style="font-size: 0.8rem;"><?php if(isLoggedin()){ echo "Inloggad: " . strval($_SESSION['username']); } else { echo "Ej inloggad"; } ?> </span> <?php if(isLoggedIn()){ ?> <input type="submit" name="loginOut" value="Logga ut" id="logoutBtn"> <?php } else { ?> <a class="backA" style="color: #1bbb85;" href="p-moment4-login.php">Inloggning</a> <a class="backA" style="color: #1bbb85;" href="p-moment4-register.php">Registrera</a> <?php }?> </form> <ul id="nav-ul2"> <li class="menu-btn2 <?php echo setCurrentBtnAdmin(1);?>"> <a href="p-moment4.php">Startsida</a></li> <li class="menu-btn2 <?php echo setCurrentBtnAdmin(2);?>"> <a href="p-moment4-new-blogposts.php">Nya inlägg</a></li> <?php if(isLoggedin()){ ?> <li class="menu-btn2 <?php echo setCurrentBtnAdmin(3);?>"> <a href="p-moment4-manage-blogposts.php">Hantera inlägg</a> </li> <?php }?> </ul> <hr> <h3 style="margin-top:10px;">Ändra blogginlägg<?php // Output blog title when it exists. if($postExist){echo " - " . htmlspecialchars($editPost[0]['blogpost_title'], ENT_QUOTES, 'UTF-8');}?></h3> <form id="form3" action="p-moment4-edit-blogpost.php?editid=<?= $_GET['editid'];?>" method="POST"> <div class="login-rows2"> <div class="login-parts"> <input id="newblogTitle" class="login-fields fieldListen" type="text" name="newBlogTitle" value="<?php if($postExist){echo htmlspecialchars($editPost[0]['blogpost_title'], ENT_QUOTES, 'UTF-8');} else{echo previousFieldvalueP('post-blogpost','newBlogTitle');}?>"> <label for="newblogTitle">Titel</label> <?php // Demand filling out empty field ONLY if not succesfully published blogpost if(!isset($_SESSION['blogpost-success'])){ fillOutEmptyFieldP("post-blogpost","newBlogTitle","Du får ej lämna blogginläggets titelfält tomt!");} // Clicked on Publicera? if(clickedP('post-blogpost')){ // Check length for between 15 and 64 characters using static public methods from Validate class. if(strlen($_POST['newBlogTitle']) != 0 && (!Validate::isLeastLength($_POST['newBlogTitle'],15)) || !Validate::isMaxAllowedLength($_POST['newBlogTitle'],64)){ // And show error on how to fix when not following it echo showError("Blogginläggets titel ska vara mellan 15-64 tecken."); } } ?> </div> <div class="login-parts"> <textarea id="newblogText" style="resize: none;" class="login-fields fieldListen textarea" name="newBlogText"><?php if($postExist){echo htmlspecialchars($editPost[0]['blogpost_text'], ENT_QUOTES, 'UTF-8');} else{echo previousFieldvalueP('post-blogpost','newBlogText');}?></textarea> <label for="newblogText">Innehåll</label> <?php // VERY IMPORTANT: <textarea> above must be on a single line of code or whitespace will appear despite otherwise being empty!!! // This is why I could not separate it with ENTER to write a comment there // Demand filling out empty field ONLY if not succesfully published blogpost if(!isset($_SESSION['blogpost-success'])){ fillOutEmptyFieldP("post-blogpost","newBlogText","Du får ej lämna blogginläggets textinnehåll tomt!");} // Clicked on Publicera? if(clickedP('post-blogpost')){ // Only allow up to 4000 characters in the text of blog article if(strlen($_POST['newBlogText']) != 0 && !Validate::isMaxAllowedLength($_POST['newBlogText'],4000)){ // And show error on how to fix when not following it echo showError("Textinnehållet får vara max 4000 tecken (motsvarar cirka 500 ord) långt."); } } ?> </div> </div> <input type="submit" name="post-blogpost" value="Ändra inlägg" id="publishBtn"> <?php // Show success message when blogpost has been changed successfully! showSessionSuccess('edit-post-success','Blogginläggets ändringar har sparats!'); ?> <?php ?> </form> <hr> <h4 style="margin-top:10px;">Befintliga blogginlägg av <?= $_SESSION['username']; ?></h4> <?php // Retrieve the two latest published blog posts $currentBlogposts = $DB->getAllBlogPosts($_SESSION['username']); // and check if 0 blog posts exist by checking length of array if(count($currentBlogposts) == 0){ // If zero blog posts exist, echo info about that echo showError('Inga blogginlägg finns. Publicera ett först!'); } // If blogposts do exist, output them with foreach() // substr() is used to only show YYYY-MM-DD HH:MM by removing :SS(seconds) // mb_substr() only shows first 200 characters of each blog post else { foreach($currentBlogposts as $blogpost){ ?> <div class="blogpostChangeDeleteRows"> <div> <p style="font-size:0.9rem; font-weight:bold;">[<?=blogPublishDateShort($blogpost['blogpost_created'])?>]: <?= htmlspecialchars($blogpost['blogpost_title'],ENT_QUOTES, 'utf-8');?> </p> </div> <div> <p style="text-align:right;"> <a class="backA" style="color: #1bbb85; font-size:0.9rem;" href="p-moment4-edit-blogpost.php?editid=<?=$blogpost['id'];?>">Ändra inlägg</a> <a class="backA" style="color: #1bbb85; font-size:0.9rem;" href="p-moment4-manage-blogposts.php?deleteid=<?=$blogpost['id'];?>">Radera inlägg</a> </p> </div> </div> <?php } } ?> <hr> </div> <?php include("include/secret.php")?> <?php include("include/c-footer.php");?>

Dold text

p-moment4-login.php

<?php include("include/config.php");?> <?php $currentBtn=7; // Mark current page in <nav> (see c-nav.php) // Clicked on login? if(clickedP('login') && !Validate::isEmpty($_POST['dataLoginName']) && !Validate::isEmpty($_POST['dataLoginPass'])){ // Store Användarnamn och Lösenord in $_POST variables $user = setPost('dataLoginName'); $pass = setPost('dataLoginPass'); // Here we assume login succeeeded if($DB->loginUser($user,$pass)){ // So go straight to admin panel $_SESSION['loginSuccess'] = 1; // Indicate what success message should be shown header("Location: p-moment4.php"); // Redirect }} // Redirect if is logged in ($_SESSION exists) already and navigated to p-moment4.php if(isLoggedIn()){header("Location: p-moment4.php");} // This is the redirected Login page so no need for CheckLoginOrRedirect() here ?> <title><?= pageTitle("Moment 4 - Ej inloggad"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2 id="specialh2">Moment 4 - Dataanslutningar med PHP | Inloggning</h2> <div class="uppgifts-div" id="special1"> <h3 style="text-align: left;">Logga in</h3> <form id="form3" action="p-moment4-login.php" method="POST"> <div class="login-rows"> <div class="login-parts"> <input id="dataLoginName" class="login-fields fieldListen" type="text" name="dataLoginName"> <label for="dataLoginName">Användarnamn</label> </div> <div class="login-parts"> <input id="dataLoginPass" class="login-fields fieldListen" type="password" name="dataLoginPass" autocomplete="off"> <label for="dataLoginPass">Lösenord</label> </div> <div style="display: flex; flex-direction: row; gap: 10px; justify-content: space-between; width: 100%;"> <input type="submit" name="login" value="Logga in" id="loginBtn"> <p><a style="display:inline-block; line-height: 3;" class="backA" href="p-moment4-register.php">Registrera</a></p> </div> </div> <?php // Clicked on Login button? if(isset($_POST['login'])){ // Are one of the login fields empty? if(Validate::isEmpty($_POST['dataLoginName']) || Validate::isEmpty($_POST['dataLoginPass'])){ // Demand both fields being filled out echo showError("Ange både användarnamn och lösenord först!<br>"); } } // Clicked on login? if(clickedP('login') && !Validate::isEmpty($_POST['dataLoginName']) && !Validate::isEmpty($_POST['dataLoginPass'])){ // Store Användarnamn och Lösenord in $_POST variables $user = setPost('dataLoginName'); $pass = setPost('dataLoginPass'); // Here we assume failed login due to wrong username/password if(!$DB->loginUser($user,$pass)){ // Thus show error on how to fix echo showError("Fel användarnamn och/eller lösenord!");} } // Arrived on page through redirect? showSessionError('redirected','Du måste logga in först!<br>'); // Arrived on page after logging out? showSessionSuccess('loggedOut','Du har loggats ut!<br>'); // Arrived by trying to log out without logout button when not logged in showSessionError('wrongLogout','Du kan inte logga in utan att vara inloggad!<br>'); // Arrived after successfully registering blog account showSessionSuccess('registerSuccess','Du kan nu logga in med registrerat användarnamn och lösenord!<br>'); ?> </form> </div> <?php include("include/secret.php")?> <?php include("include/c-footer.php");?>

Dold text

p-moment4-logout.php

<?php // Start session to find logged in user session_start(); // Log out user only if they clicked on log out button if(isset($_POST['loginOut']) && isset($_SESSION['username'])){ // and unset session for Username which keeps user logged in unset($_SESSION['username']); // Then destroy entire session session_destroy(); // Start new session and store session loggedOut so logged // out message can be shown on login page after logging out session_start(); $_SESSION['loggedOut'] = 1; // Redirect to login page header("Location: p-moment4-login.php"); } // User did not click log out but is logged in if(!isset($_POST['loginOut']) && isset($_SESSION['username'])){ $_SESSION['logOutWithoutBtn'] = 1; // Set session to show error message on admin page header("Location: p-moment4.php"); } // User did not click log out and is not logged in if(!isset($_POST['loginOut']) && !isset($_SESSION['username'])){ header("Location: p-moment4.php"); // Set session to show error message on login page $_SESSION['wrongLogout'] = 1; } ?>

Dold text

p-moment4-manage-blogposts.php

<?php include("include/config.php");?> <?php $currentBtnAdmin=3; $currentBtn=7; // Mark current page in <nav> (see c-nav.php) // Redirect to login page if not logged in and set Session error message to be shown there checkLoginOrRedirect(); // Is Logged in AND Clicked on a "Radera inlägg" link? if(clickedG('deleteid')){ // Get integer value from clicked "Radera inlägg" link $blogpost = intval(setGet('deleteid')); // Try delete correct chosen blogpost by correct username if($DB->deleteDBRow($_SESSION['username'],"blogposts",$blogpost)){ // If true, meaning it could delete, prepare success message $_SESSION['delete-post-success'] = 1; unset($_GET['deleteid']); // and unset the value of deleteid } // When failed to delete (trying to delete a post that does not exist) else { // Prepare fail message and unset value of deleteid $_SESSION['delete-post-fail'] = 1; unset($_GET['deleteid']);} } // Is Logged in AND Clicked on Publicera successfully? if(clickedP('post-blogpost')){ // Now check several stuff before publishing if(strlen($_POST['newBlogTitle']) != 0 // Blog title not zero characters long && Validate::isLeastLength($_POST['newBlogTitle'],15) // Blog title at least 15 characters long && Validate::isMaxAllowedLength($_POST['newBlogTitle'],64) // Blog title at most 64 characters long && strlen($_POST['newBlogText']) != 0 // Blog text not zero characters long && Validate::isMaxAllowedLength($_POST['newBlogText'],4000) // Blog text at most 4000 characters long ) // When ALL above is TRUE then publish { // Store $_POST variables and current user for easier SQLing $title = $_POST['newBlogTitle']; // blog title $text = $_POST['newBlogText']; // blog text $user = $_SESSION['username']; // current logged in user // Insert into Database using insertDB function which first takes name of table (blogposts) and then an infinite amount of // arguments. It takes the columns and then the values for them. So first send name of columns and then the values which // would be variables $title, $text and $user in this case! if($DB->insertDB("blogposts","blogpost_title","blogpost_text","blogpost_created_by",$title,$text,$user)){ // If succeeds then prepare session to show success message further down and not showing errors when emptying filled out fields $_SESSION['blogpost-success'] = 1; // Because we successfully published a blog post we can erase the otherwise filled out fields $_POST['newBlogTitle'] = ""; // Such as "Titel" $_POST['newBlogText'] = ""; // And "Innehåll" } else{ // Otherwise prepare failure message to show $_SESSION['blogpost-failure'] = 1; } } } ?> <title><?php if(isLoggedIn()){ echo pageTitle("Moment 4 - " . strval($_SESSION['username']) . " inloggad"); }else { echo pageTitle("Moment 4 - Ej inloggad");}?> </title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2 id="specialh2">Moment 4 - Dataanslutningar med PHP | <?php echo isLoggedIn() ? strval($_SESSION['username']) . " inloggad" : " Ej inloggad" ?></h2> <div class="uppgifts-div" id="special1"> <form action="p-moment4-logout.php" method="POST" id="logoutBtnSection"> <?php // Show success message when blogpost with deleteid=X could be deleted! showSessionSuccess('delete-post-success','Blogginlägget har raderats!'); // Show failure message when blogpost with deleteid=X could NOT be deleted! showSessionError('delete-post-fail','Blogginlägg saknas eller du saknar tillstånd att radera!'); // Show failure message when trying to logout without using log out button showSessionError('logOutWithoutBtn','Logga ut med Logga ut-knappen!'); ?> <span style="font-size: 0.8rem;"><?php if(isLoggedin()){ echo "Inloggad: " . strval($_SESSION['username']); } else { echo "Ej inloggad"; } ?> </span> <?php if(isLoggedIn()){ ?> <input type="submit" name="loginOut" value="Logga ut" id="logoutBtn"> <?php } else { ?> <a class="backA" style="color: #1bbb85;" href="p-moment4-login.php">Inloggning</a> <a class="backA" style="color: #1bbb85;" href="p-moment4-register.php">Registrera</a> <?php }?> </form> <ul id="nav-ul2"> <li class="menu-btn2 <?php echo setCurrentBtnAdmin(1);?>"> <a href="p-moment4.php">Startsida</a></li> <li class="menu-btn2 <?php echo setCurrentBtnAdmin(2);?>"> <a href="p-moment4-new-blogposts.php">Nya inlägg</a></li> <?php if(isLoggedin()){ ?> <li class="menu-btn2 <?php echo setCurrentBtnAdmin(3);?>"> <a href="p-moment4-manage-blogposts.php">Hantera inlägg</a> </li> <?php }?> </ul> <?php showSessionError('id-error','Du kan inte visa inget inlägg!');?> <hr> <h3 style="margin-top:10px;">Hantera inlägg - Skriva inlägg</h3> <h4 style="margin-top:10px;">Skriv nytt blogginlägg</h4> <form id="form3" action="p-moment4-manage-blogposts.php" method="POST"> <div class="login-rows2"> <div class="login-parts"> <input id="newblogTitle" class="login-fields fieldListen" type="text" name="newBlogTitle" value="<?= previousFieldvalueP('post-blogpost','newBlogTitle'); ?>"> <label for="newblogTitle">Titel</label> <?php // Demand filling out empty field ONLY if not succesfully published blogpost if(!isset($_SESSION['blogpost-success'])){ fillOutEmptyFieldP("post-blogpost","newBlogTitle","Ange en titel för blogginlägget!");} // Clicked on Publicera? if(clickedP('post-blogpost')){ // Check length for between 15 and 64 characters using static public methods from Validate class. if(strlen($_POST['newBlogTitle']) != 0 && (!Validate::isLeastLength($_POST['newBlogTitle'],15)) || !Validate::isMaxAllowedLength($_POST['newBlogTitle'],64)){ // And show error on how to fix when not following it echo showError("Blogginläggets titel ska vara mellan 15-64 tecken."); } } ?> </div> <div class="login-parts"> <textarea id="newblogText" style="resize: none;" class="login-fields fieldListen textarea" name="newBlogText"><?= previousFieldvalueP('post-blogpost','newBlogText');?></textarea> <label for="newblogText">Innehåll</label> <?php // VERY IMPORTANT: <textarea> above must be on a single line of code or whitespace will appear despite otherwise being empty!!! // This is why I could not separate it with ENTER to write a comment there // Demand filling out empty field ONLY if not succesfully published blogpost if(!isset($_SESSION['blogpost-success'])){ fillOutEmptyFieldP("post-blogpost","newBlogText","Skriv textinnehåll för blogginlägget!");} // Clicked on Publicera? if(clickedP('post-blogpost')){ // Only allow up to 4000 characters in the text of blog article if(strlen($_POST['newBlogText']) != 0 && !Validate::isMaxAllowedLength($_POST['newBlogText'],4000)){ // And show error on how to fix when not following it echo showError("Textinnehållet får vara max 4000 tecken (motsvarar cirka 500 ord) långt."); } } ?> </div> </div> <input type="submit" name="post-blogpost" value="Publicera inlägg" id="publishBtn"> <?php // Did we manage to post a blog bost? Then show success message and unset $_SESSION showSessionSuccess('blogpost-success',"Du har publicerat blogginlägget!"); ?> <?php ?> </form> <hr> <h4 style="margin-top:10px;">Befintliga blogginlägg av <?= $_SESSION['username']; ?></h4> <?php // Retrieve the two latest published blog posts $currentBlogposts = $DB->getAllBlogPosts($_SESSION['username']); // and check if 0 blog posts exist by checking length of array if(count($currentBlogposts) == 0){ // If zero blog posts exist, echo info about that echo showError('Inga blogginlägg finns. Publicera ett först!'); } // If blogposts do exist, output them with foreach() // substr() is used to only show YYYY-MM-DD HH:MM by removing :SS(seconds) // mb_substr() only shows first 200 characters of each blog post else { foreach($currentBlogposts as $blogpost){ ?> <div class="blogpostChangeDeleteRows"> <div> <p style="font-size:0.9rem; font-weight:bold;">[<?=blogPublishDateShort($blogpost['blogpost_created'])?>]: <?= htmlspecialchars($blogpost['blogpost_title'], ENT_QUOTES, 'UTF-8');?> </p> </div> <div> <p style="text-align:right;"> <a class="backA" style="color: #1bbb85; font-size:0.9rem;" href="p-moment4-edit-blogpost.php?editid=<?=$blogpost['id'];?>">Ändra inlägg</a> <a class="backA" style="color: #1bbb85; font-size:0.9rem;" href="p-moment4-manage-blogposts.php?deleteid=<?=$blogpost['id'];?>">Radera inlägg</a> </p> </div> </div> <?php } } ?> <hr> </div> <?php include("include/secret.php")?> <?php include("include/c-footer.php");?>

Dold text
Visa signatur

<WKL:"En kodrad i taget!";/>