Compare commits
29 Commits
324f310e38
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 88af050bea | |||
| 2373233899 | |||
| 8239d8024d | |||
| 1586d02094 | |||
| 2fe4bab887 | |||
| eb792edb4d | |||
| 1a450491c6 | |||
| a472e34bbe | |||
| 0490924e3d | |||
| db7896d2de | |||
| bd2243e28a | |||
| 1c7d7f636a | |||
| 1fe20f3f3b | |||
| 7f4884a90d | |||
| 532f73309c | |||
| 129a0c321c | |||
| ef4ddb1420 | |||
| da6a720f46 | |||
| 6d7a783d73 | |||
| 6cb1a005fd | |||
| d99fa137ec | |||
| 534fc8ace1 | |||
| 783e91969c | |||
| 226c55aca3 | |||
| 755c3d4e2e | |||
| c875a397ad | |||
| 5034bf1248 | |||
| 322c2a7e7f | |||
| 7aa9c51104 |
BIN
R1.02/cours/js.pdf
Normal file
43
R3.01/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Programmation WEB coté serveur R3.01
|
||||
Ce dépôt contient les ressources utilisées
|
||||
(cours/td/tp) dans la ressource R3.01.
|
||||
|
||||
**Les objectifs**
|
||||
|
||||
> Savoir développer une application Web côté serveur, en utilisant le langage PHP.
|
||||
|
||||
Les notions suivantes seront abordées :
|
||||
|
||||
- Le langage php
|
||||
On se limitera aux bases du langages. La couche objet sera présenté très succintement.
|
||||
Toute la suite sera illustré avec PHP.
|
||||
|
||||
- Interaction avec le client
|
||||
URL (Uniform Resource Locator), requêtes, formulaires, transmission des paramètres, des données, etc.
|
||||
|
||||
- Applications Web à états
|
||||
Cookies et sessions
|
||||
|
||||
- Organisation de l’accès aux données
|
||||
Bases de données, annuaires, services Web, etc.
|
||||
|
||||
- Introduction à la programmation objet en PHP
|
||||
Introduction au principe MVC
|
||||
|
||||
- Sensibilisation à la sécurité des applications web
|
||||
|
||||
## Calendrier
|
||||
|
||||
| Semaine | Cours | TD/TP |
|
||||
| ------------------ | -------------------------------------------------------- | ------------------ |
|
||||
| 1 : 16/03 - 20/03| [Bases du langages](./cours/cm_bases_php.pdf) | |
|
||||
|
||||
## Les TPS
|
||||
|
||||
#### TP1 : Bases du langage PHP
|
||||
Le [tp1](./tp/tp1)
|
||||
permet de se familiariser avec le langage PHP.
|
||||
À chaque exercice correspond un sous répertoire avec
|
||||
des fichiers à compléter.
|
||||
|
||||
|
||||
BIN
R3.01/cours/cm_bases_php.pdf
Normal file
@@ -6,4 +6,17 @@
|
||||
#### Semaine 2
|
||||
[DOM](cours/dom.pdf), [tp2](./td_tp/tp2), [tp2mvc](td_tp/tp2mvc)
|
||||
|
||||
#### Semaine 3
|
||||
[DOM](cours/dom.pdf), [tp3](./td_tp/tp3)
|
||||
|
||||
#### Semaine 4
|
||||
[Promesses, Ajax, API de données](cours/ajax.pdf), [tp4](./td_tp/tp4)
|
||||
|
||||
#### Semaine 5
|
||||
[Programmation déclarative, RIOT.js](cours/riot.pdf), [tp5](./td_tp/tp5)
|
||||
#### Semaine 6
|
||||
[API REST](cours/api.pdf), service firebase [tp6](./td_tp/tp6)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
R4.01_R4.A.10/cours/ajax.pdf
Normal file
BIN
R4.01_R4.A.10/cours/api.pdf
Normal file
BIN
R4.01_R4.A.10/cours/riot.pdf
Normal file
54
R4.01_R4.A.10/miniprojet/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
## Thème
|
||||
|
||||
Écrire une application web en javascript qui permet de consulter les données de
|
||||
parcours sup (vœux de poursuite d'études et de réorientation dans l'enseignement
|
||||
supérieur et réponses des établissements) afin d'aider un(e) lycéén(e) à faire ses choix.
|
||||
|
||||
|
||||
- La partie cliente devra être réaliser au moyen du framework [riotjs](https://riot.js.org/).
|
||||
- Les données seront consommées au moyen de [l'api](https://data.enseignementsup-recherche.gouv.fr/explore/dataset/fr-esr-parcoursup/api/?timezone=Europe%2FBerlin&sort=tri) opendata. (cf [méthodologie](https://data.enseignementsup-recherche.gouv.fr/api/datasets/1.0/fr-esr-parcoursup/attachments/methodologie_opendata_2025pdf/))
|
||||
- La localisation utilisera openstreet map et l'api [Leaflet](https://leafletjs.com/).
|
||||
- Les graphiques seront réalisés au moyen de [chart css](https://chartscss.org/).
|
||||
|
||||
## Fonctionnalités
|
||||
#### Recherche des formations par mots clés, avec localisation sur une carte.
|
||||
|
||||
[<a href="img/search.png">exemple</a>]
|
||||
#### Vue d'une formation particulière, avec accès aux données de parcourssup, entre autres :
|
||||
- les données lors de la phase d'admission,
|
||||
- les données lors de la phase complémentaire,
|
||||
- le profils des admis,
|
||||
- l'évolution de la selectivité, des mentions au bac depuis 2020.
|
||||
|
||||
[<a href="img/formation.png">exemple</a>, <a href="img/profil.png">exemple</a>,
|
||||
<a href="img/profil1.png">exemple</a>]
|
||||
|
||||
#### Aide au choix d'orientation
|
||||
|
||||
L'utilisateur doit pouvoir selectionner des formations/filières afin de les comparer et estimer
|
||||
ses chances d'intégrations en fonction de la série de son bac et de sa moyenne en terminale.
|
||||
|
||||
[<a href="img/selection1.png">exemple</a>
|
||||
, <a href="img/selection2.png">exemple</a>]
|
||||
|
||||
Les choix de l'utilisateur devront au moins être persistant localement. Vous pouvez, losque tout fonctionne,
|
||||
mettre en oeuvre une persistance avec [firebase](https://firebase.com).
|
||||
|
||||
## Réalisation
|
||||
1. Une partie de votre travail consiste à comprendre comment fonctionne l'api (entrées/sorties), et comment l'utiliser. N'hésitez pas à encapsuler/abstraire les informations de l'api dans votre propre modèle.
|
||||
2. Le but est d'utiliser la programmation déclarative et par composants de RIOT.js. Il faut donc l'architecturer et l'organiser en
|
||||
conséquence. En particulier, il faut profiter pleinement de l'utilisation de composants le plus génériques possibles.
|
||||
3. Il faut utiliser des urls routables. Riot.js vient avec un [router](https://github.com/riot/route) très simple d'utilisation.
|
||||
Je vous expliquerai comment mettre en oeuvre la réécriture d'url avec apache.
|
||||
4. Il est possible d'utiliser le pattern observable, avec une bibliothèque adhoc ([exemple](https://github.com/riot/observable))
|
||||
5. Dans le depot GIT de votre projet, il faut une description des composants, leurs entrées, leurs fonctions, et comment
|
||||
ils communiquent avec le reste du monde.
|
||||
|
||||
## Attendus
|
||||
Un mail (sujet S4WEB) m'indiquant les informations suivantes :
|
||||
|
||||
- l'url de votre projet sous la forme : http://dwarves.iut-fbleau.fr/~login/parcoursup
|
||||
- l'url du dépôt GIT avec vos sources sur dwarves.iut-fbleau.fr
|
||||
- les noms du binôme.
|
||||
|
||||
Le tout est à finaliser avant le Vendredi 03 avril 2025, 18 heures. Une soutenance aura lieu la semaine du 30/03.
|
||||
17
R4.01_R4.A.10/miniprojet/groupes.md
Normal file
@@ -0,0 +1,17 @@
|
||||
## Groupes projet
|
||||
- Rayan Lankechari / Ibrahima Bah
|
||||
- Demrici-Özmen Canpolat / Eliot Maxime
|
||||
- Ozvann ABRAHAM / Karl FRAMERY
|
||||
- SOLAR Dimitri / ZAABAY Tarehi
|
||||
- Nicolas Miekisiak / Loïc Sainton
|
||||
- Torreblanca Mathys / Poitou Enzo
|
||||
- WIECZOREK Valentin / PLOUVIER Luka
|
||||
- Kouami Kpeglo / Alexis Fort
|
||||
- Ugo Faye / Mathis Leclere
|
||||
- Nathan Couchot / Théo Anastasio / Adrien Rabot
|
||||
- Pierre COURTEHOUX / Lukas SIMOES
|
||||
- CAMILLE Cléo / Warrhen Roland
|
||||
- Aylane Sehl / Séri-Khane / ??
|
||||
- Arwa Ben Fraj / Bounni Loubelo Benicia
|
||||
- Hicham Cherifi / Wael Atik
|
||||
- DERQSI BILAL / CAMILLE LECHAVLIER
|
||||
BIN
R4.01_R4.A.10/miniprojet/img/formation.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/profil.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/profil1.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/search.png
Normal file
|
After Width: | Height: | Size: 238 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/selection1.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/selection2.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
100
R4.01_R4.A.10/td_tp/tp4/README.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# TP javascript : Ajax, promesses.
|
||||
|
||||
Vous disposez tous, à la racine de votre compte, d'un répertoire `public_html` qui est servi (http/https) par le serveur dwarves.
|
||||
Pour accéder à vos pages, utilisez l'url :
|
||||
|
||||
> http(s)://dwarves.[arda|iut-fbleau.fr]/~login
|
||||
|
||||
Pour tester les différentes api http, vous pouvez utilisez la commande `curl`.
|
||||
|
||||
|
||||
#### Ex1
|
||||
Le but est de faire une recherche de film en utilisant l'api [OMDb](https://www.omdbapi.com/) (Open Movie Database).
|
||||
|
||||

|
||||
|
||||
Son utilisation nécessite une clé. Vous pouvez utiliser la mienne `2fcb2848`. Si
|
||||
la limite de requêtes est atteinte, générez votre propre clé.
|
||||
|
||||
|
||||
Complétez le [code](./src/ex1).
|
||||
- J'ai volontairement utilisé des modules. Il vous faut donc tester l'application avec
|
||||
http.
|
||||
|
||||
- Écrire la fonction du modèle
|
||||
```js
|
||||
getMovies(search)
|
||||
```
|
||||
Cette fonction renvoie une promesse qui permet de faire une requête ajax vers l'api OMDb. Pour ajax,
|
||||
utilisez d'abord l'interface `fetch`, ~~puis l'objet `XMLHttpRequest`~~.
|
||||
|
||||
- Écrire la fonction du contrôleur
|
||||
```js
|
||||
async search(searchMovie)
|
||||
```
|
||||
qui permet de faire une recherche. Cette fonction utilise évidemment la fonction précédente `getMovies`.
|
||||
|
||||
- Écrire la fonction de la vue
|
||||
```js
|
||||
renderList(movies)
|
||||
```
|
||||
qui construit la liste des films affichés. La structure attendue est
|
||||
```html
|
||||
<li>
|
||||
<a href="http://www.imdb.com/title/tt0068646" target="_blank">The Godfather</a><span>1972</span>
|
||||
</li>
|
||||
```
|
||||
#### Ex2
|
||||
Le but, dans un premier temps, est d'écrire une interface, avec ajax, qui permette de jouer au jeu suivant :
|
||||
|
||||
- On a une grille dans laquelle sont cachés des renards (on en connait le nombre initial).
|
||||
|
||||
- A chaque tour, on choisit de tirer sur un case. On a l'alternative suivante :
|
||||
- on tue un renard,
|
||||
- on récupére les nombres de renards sur la grille sur la ligne, colonne et diagonales de la case.
|
||||
|
||||
<div align="center">
|
||||
<img src="img/renard1.png">
|
||||
</div>
|
||||
|
||||
Le jeu utilise un serveur http à l'url suivante
|
||||
|
||||
```
|
||||
https://dwarves.iut-fbleau.fr/foxes/foxes.php
|
||||
```
|
||||
|
||||
Voici les urls du jeu :
|
||||
|
||||
```
|
||||
https://dwarves.iut-fbleau.fr/foxes/foxes.php?new&size=10&foxes=10
|
||||
```
|
||||
|
||||
initialise la partie , avec la taille de la grille et le nombre de renards
|
||||
dont la position est aléatoire (le script utilise des sessions de cinq minutes).
|
||||
|
||||
La réponse normale, en json :
|
||||
|
||||
```json
|
||||
{"status":"ok","foxes":10,"tries":0}
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
https://dwarves.iut-fbleau.fr/foxes/foxes.php?X=1&Y=3
|
||||
```
|
||||
|
||||
permet de tirer sur une case. Le serveur répond
|
||||
|
||||
```json
|
||||
{"status":"ok","foxes":2,"tries":1}
|
||||
```
|
||||
|
||||
`foxes` vaut -1 si le coup est gagnant, sinon donne le nombre de renards de la colonne, ligne et diagonales de la case.
|
||||
|
||||
|
||||
En cas de victoire, le status est `win`. En cas d'erreur, le status est `nok`.
|
||||
|
||||
|
||||
**Remarque**
|
||||
|
||||
Ajouter l'option `credentials` à vos requêtes pour utiliser la session en cours.
|
||||
35
R4.01_R4.A.10/td_tp/tp4/aide.md
Normal file
@@ -0,0 +1,35 @@
|
||||
#### XMLHttpRequest
|
||||
```js
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open("GET","mon/url")
|
||||
|
||||
xhr.onload = ...
|
||||
xhr.onerror = ...
|
||||
|
||||
xhr.send()
|
||||
```
|
||||
#### Promise
|
||||
```js
|
||||
|
||||
let promise = new Promise ((resolve,reject) => {
|
||||
setTimeout(() => {
|
||||
resolve("OK")
|
||||
},1000)
|
||||
})
|
||||
promise.then(data => ....)
|
||||
```
|
||||
|
||||
#### async/await
|
||||
|
||||
```js
|
||||
|
||||
function getUser(email){
|
||||
return new Promise(...)
|
||||
}
|
||||
|
||||
async function updateUser(){
|
||||
|
||||
let user = await getUser(...)
|
||||
console.log(user)
|
||||
}
|
||||
```
|
||||
BIN
R4.01_R4.A.10/td_tp/tp4/img/renard1.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
R4.01_R4.A.10/td_tp/tp4/img/search.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
86
R4.01_R4.A.10/td_tp/tp4/src/ex1/css/style.css
Normal file
@@ -0,0 +1,86 @@
|
||||
main {
|
||||
position: absolute;
|
||||
top: 2rem;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
.loader {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
margin: 6rem 0 0;
|
||||
}
|
||||
.error {
|
||||
color: #FFFAAA;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
label {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
label span {
|
||||
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
|
||||
}
|
||||
input {
|
||||
margin: 1rem 0 0;
|
||||
font-size: 1.6rem;
|
||||
font-weight: 300;
|
||||
padding: 0.8rem 1rem;
|
||||
color: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
transition: all 0.3s;
|
||||
box-shadow: 1px 1px 2px rgba(0,0,0, 0.3);
|
||||
-moz-appearance:none;
|
||||
-webkit-appearance:none;
|
||||
outline: none;
|
||||
}
|
||||
input:focus {
|
||||
border: 1px solid transparent;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 1rem 0 2rem;
|
||||
}
|
||||
ul li {
|
||||
padding: 0.6rem 1rem;
|
||||
margin: 1px 0;
|
||||
line-height: 1.4rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
align-content: space-between;
|
||||
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 0 2px rgba(0,0,0, 0.3);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
ul li:hover,
|
||||
ul li:active,
|
||||
ul li:focus {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
ul li a {
|
||||
margin: 0 0.6rem 0 0;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
ul li span {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial;
|
||||
font-weight: 300;
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
background-image: -webkit-radial-gradient(ellipse farthest-corner at top, #661141, #000000);
|
||||
background-image: radial-gradient(ellipse farthest-corner at top, #661141, #000000);
|
||||
color: white;
|
||||
}
|
||||
13
R4.01_R4.A.10/td_tp/tp4/src/ex1/img/puff.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL -->
|
||||
<svg width="44" height="44" viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg" stroke="#fff">
|
||||
<g fill="none" fill-rule="evenodd" stroke-width="2">
|
||||
<circle cx="22" cy="22" r="1">
|
||||
<animate attributeName="r" begin="0s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite"/>
|
||||
<animate attributeName="stroke-opacity" begin="0s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="22" cy="22" r="1">
|
||||
<animate attributeName="r" begin="-0.9s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite"/>
|
||||
<animate attributeName="stroke-opacity" begin="-0.9s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
28
R4.01_R4.A.10/td_tp/tp4/src/ex1/index.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="initial-scale=1,width=device-width">
|
||||
<link rel="stylesheet" type="text/css" href="./css/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- vue html -->
|
||||
<main>
|
||||
<label>
|
||||
<span>Search a movie</span>
|
||||
<input type="input" placeholder="28 Days Later..">
|
||||
</label>
|
||||
|
||||
<div class="loader">
|
||||
<img src="./img/puff.svg">
|
||||
</div>
|
||||
|
||||
<p class="error"></p>
|
||||
<div id="list-movies">
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
<script type="module" src="./module/app.js"></script>
|
||||
</html>
|
||||
7
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/app.js
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
import model from '../module/model.js'
|
||||
import Controller from '../module/controller.js'
|
||||
import View from '../module/view.js'
|
||||
|
||||
const view = new View()
|
||||
const app = new Controller(view,model)
|
||||
26
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/controller.js
Normal file
@@ -0,0 +1,26 @@
|
||||
class Controller {
|
||||
constructor(view,model){
|
||||
this.view = view
|
||||
this.model = model
|
||||
|
||||
this.loading = false
|
||||
this.lastSearch = null
|
||||
this.error = null
|
||||
this.results = []
|
||||
|
||||
this.view.setLoading(false)
|
||||
this.view.bindSearch(this.search.bind(this))
|
||||
}
|
||||
reset() {
|
||||
this.loading = false
|
||||
this.error = null
|
||||
this.results = []
|
||||
}
|
||||
|
||||
async search(searchMovie) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
export default Controller
|
||||
|
||||
12
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/helpers.js
Normal file
@@ -0,0 +1,12 @@
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds.
|
||||
function debounce(fn, wait) {
|
||||
let timeout
|
||||
|
||||
return (...args) => {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => fn(...args), wait)
|
||||
}
|
||||
}
|
||||
export default debounce
|
||||
9
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/model.js
Normal file
@@ -0,0 +1,9 @@
|
||||
let apiKey = '2fcb2848'
|
||||
|
||||
let model = {
|
||||
getMovies(search){
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
export default model
|
||||
44
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/view.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import debounce from "./helpers.js"
|
||||
|
||||
class View {
|
||||
constructor(){
|
||||
this.listMovies = document.querySelector("#list-movies")
|
||||
this.inputSearch = document.querySelector("input")
|
||||
this.loader = document.querySelector(".loader")
|
||||
this.message = document.querySelector("p.error")
|
||||
}
|
||||
|
||||
#getInput(){
|
||||
return this.inputSearch.value
|
||||
}
|
||||
|
||||
setLoading(loading){
|
||||
if (loading)
|
||||
this.loader.style.display = "block"
|
||||
else
|
||||
this.loader.style.display = "none"
|
||||
}
|
||||
|
||||
renderMessage(error){
|
||||
this.message.style.display = "block"
|
||||
this.message.textContent = error
|
||||
}
|
||||
|
||||
renderList(movies){
|
||||
let ul = document.createElement("ul")
|
||||
|
||||
movies.forEach((movie)=>{
|
||||
// TODO
|
||||
})
|
||||
|
||||
this.listMovies.replaceChildren(ul)
|
||||
}
|
||||
|
||||
bindSearch(handler){
|
||||
this.inputSearch.addEventListener("input",debounce((e)=>{
|
||||
handler(this.#getInput())
|
||||
},500))
|
||||
}
|
||||
}
|
||||
|
||||
export default View
|
||||
36
R4.01_R4.A.10/td_tp/tp4/src/ex2/app.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import loader from './loader.js';
|
||||
const board = document.querySelector("div.board");
|
||||
const form = document.querySelector("form");
|
||||
const message = document.querySelector("#message");
|
||||
const ld = new loader(document.getElementById("loader"));
|
||||
|
||||
let url = 'https://dwarves.iut-fbleau.fr/foxes/foxes.php';
|
||||
|
||||
let foxes;
|
||||
let size;
|
||||
|
||||
let newBoard = ( (size ) => {
|
||||
let frag = document.createDocumentFragment();
|
||||
for (let i = 0; i< size; i++){
|
||||
let div = document.createElement("div");
|
||||
for(let j = 0; j < size; j++){
|
||||
let span = document.createElement("span");
|
||||
span.dataset.num = size*i + j;
|
||||
div.appendChild(span);
|
||||
}
|
||||
frag.appendChild(div);
|
||||
}
|
||||
board.replaceChildren(frag);
|
||||
})
|
||||
|
||||
|
||||
|
||||
form.addEventListener("submit", (async (ev) => {
|
||||
ev.preventDefault();
|
||||
// TODO
|
||||
}))
|
||||
|
||||
|
||||
board.addEventListener("click", ( async (ev) => {
|
||||
// TODO
|
||||
}))
|
||||
54
R4.01_R4.A.10/td_tp/tp4/src/ex2/css/style.css
Normal file
@@ -0,0 +1,54 @@
|
||||
article {
|
||||
text-align : center;
|
||||
}
|
||||
.board > div {
|
||||
display : flex;
|
||||
}
|
||||
article .board {
|
||||
display : inline-block;
|
||||
}
|
||||
.board span {
|
||||
display: inline-block;
|
||||
vertical-align:baseline;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
/*padding : 1em;*/
|
||||
margin : 0.1em;
|
||||
cursor: pointer;
|
||||
border: 1px solid #444;
|
||||
/*background: radial-gradient(rgb(150,250,150), #00ee00);*/
|
||||
}
|
||||
.board span:hover {
|
||||
background: #3AB903;
|
||||
background: radial-gradient(rgb(66, 255, 66), #065006);
|
||||
}
|
||||
|
||||
.board span.on {
|
||||
background: #009900;
|
||||
}
|
||||
|
||||
div.is-loading {
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
overflow: show;
|
||||
margin: auto;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
div.is-loading:after {
|
||||
animation: spinAround 500ms infinite linear;
|
||||
border: 2px solid hsl(0deg, 0%, 86%);
|
||||
border-radius: 9999px;
|
||||
border-right-color: transparent;
|
||||
border-top-color: transparent;
|
||||
content: "";
|
||||
display: block;
|
||||
position: relative;
|
||||
top: calc(50% - 5em);
|
||||
left: calc(50% - 5em);
|
||||
width: 10em;
|
||||
height: 10em;
|
||||
border-width: 0.25em;
|
||||
}
|
||||
35
R4.01_R4.A.10/td_tp/tp4/src/ex2/foxes.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"
|
||||
>
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div id="loader" ></div>
|
||||
|
||||
<hgroup>
|
||||
<h4>Foxes</h4>
|
||||
<p>Le but est de tuer tous les renards en minimisant le nombre de coups joués.</p>
|
||||
</hgroup>
|
||||
<p id="message"></p>
|
||||
<article>
|
||||
<div class="board">
|
||||
</div>
|
||||
</article>
|
||||
<form>
|
||||
<fieldset role="group">
|
||||
<input type="text" name="size" placeholder="Size">
|
||||
<input type="text" name="foxes" placeholder="Foxes">
|
||||
<input type="submit" value="New Game">
|
||||
</fieldset>
|
||||
</form>
|
||||
</main>
|
||||
<script type="module" src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
12
R4.01_R4.A.10/td_tp/tp4/src/ex2/loader.js
Normal file
@@ -0,0 +1,12 @@
|
||||
class loader {
|
||||
constructor(node){
|
||||
this.node = node
|
||||
}
|
||||
set(loading){
|
||||
if (loading)
|
||||
this.node.classList.add("is-loading")
|
||||
else
|
||||
this.node.classList.remove("is-loading")
|
||||
}
|
||||
}
|
||||
export default loader
|
||||
39
R4.01_R4.A.10/td_tp/tp5/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# TP javascript : riot.js
|
||||
|
||||
La [documentation](https://riot.js.org/) de riot.js
|
||||
|
||||
#### Ex1
|
||||
Voici un [exemple](./src/ex1) d'application (todo list) écrite avec riot.js.
|
||||
|
||||
<div align="center">
|
||||
<img src="./img/todo.png">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
Le but est de comprendre le code pour prendre en main Riot.js
|
||||
|
||||
- Complétez la fonction `add` qui permet d'ajouter une tâche.
|
||||
- Faites en sorte que le bouton `clear done` soit desactivé quand il n'y a pas de tâches accomplies.
|
||||
- Complétez la fonction `clear` qui retire les tâches accomplies.
|
||||
- Mettez en oeuvre les filtres `All`, `Active` et `Done`.
|
||||
- Complétez le code qui permet de supprimer une tâche.
|
||||
- Ajoutez le nombre de tâches restantes.
|
||||
- Modifiez le code pour que les tâches soient stockées localement (utilisez l'objet `localStorage`).
|
||||
|
||||
#### Ex2
|
||||
Voici un [exemple](./src/ex2) d'application (radar chart) écrite avec riot.js.
|
||||
<div align="center">
|
||||
<img src="./img/radar.png">
|
||||
</div>
|
||||
|
||||
- Complétez les méthodes `add` et `remove`.
|
||||
- Créez un nouveau composant `slider.riot`, qui vous utiliserez dans app.riot
|
||||
|
||||
#### Ex3
|
||||
L'api DummyJSON permet de manipuler des données fictives. Ecrire, avec riotjs, une application qui permet :
|
||||
|
||||
- de récuperer depuis l'url `https://dummyjson.com/products` une liste de produits
|
||||
- pour chaque produit, afficher le titre, prix et image.
|
||||
- ajouter un filtre `prix < 100`, et un bouton `voir détails`.
|
||||
|
||||
BIN
R4.01_R4.A.10/td_tp/tp5/img/radar.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
R4.01_R4.A.10/td_tp/tp5/img/todo.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
28
R4.01_R4.A.10/td_tp/tp5/src/ex1/index.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Riot todo</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="stylesheet" href="todo.css">
|
||||
<script src="todo.riot" type="riot"></script>
|
||||
<script src="https://unpkg.com/riot@10.1.2/riot+compiler.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<todo />
|
||||
</body>
|
||||
|
||||
<script>
|
||||
riot.compile().then(() => {
|
||||
riot.mount('todo', {
|
||||
title: 'I want to behave!',
|
||||
todos: [
|
||||
{ title: 'Avoid excessive caffeine', done: false ,id : 0},
|
||||
{ title: 'Be less provocative', done: true , id:1},
|
||||
{ title: 'Be nice to people', done: true, id:2}
|
||||
]
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
112
R4.01_R4.A.10/td_tp/tp5/src/ex1/todo.css
Normal file
@@ -0,0 +1,112 @@
|
||||
|
||||
body {
|
||||
font-family: 'myriad pro', sans-serif;
|
||||
font-size: 20px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
todo {
|
||||
display: block;
|
||||
max-width: 500px;
|
||||
margin: 5% auto;
|
||||
}
|
||||
|
||||
form input {
|
||||
font-size: 85%;
|
||||
padding: .4em;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #1FADC5;
|
||||
border: 1px solid rgba(0,0,0,.2);
|
||||
font-size: 75%;
|
||||
color: #fff;
|
||||
padding: .4em 1.2em;
|
||||
border-radius: 2em;
|
||||
cursor: pointer;
|
||||
margin: 0 .23em;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
background-color: #ddd;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
padding: .2em 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.completed {
|
||||
text-decoration: line-through;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
.filters {
|
||||
margin-top: 10px;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
/* position: absolute;*/
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.filters li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.filters li a,.filters li span {
|
||||
color: inherit;
|
||||
/* margin: 3px;*/
|
||||
margin-right:10px;
|
||||
padding: 3px 7px;
|
||||
text-decoration: none;
|
||||
font-size : 0.75em;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.filters li a:hover {
|
||||
border-color: rgba(175, 47, 47, 0.1);
|
||||
}
|
||||
|
||||
.filters li a.selected {
|
||||
border-color: rgba(175, 47, 47, 0.2);
|
||||
}
|
||||
|
||||
li .destroy {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
bottom: 0;
|
||||
/* width: 40px;
|
||||
height: 40px;
|
||||
margin: auto 0;
|
||||
font-size: 30px;*/
|
||||
color: #cc9a9a;
|
||||
/* margin-bottom: 11px;*/
|
||||
transition: color 0.2s ease-out;
|
||||
}
|
||||
|
||||
li .destroy:hover {
|
||||
color: #af5b5e;
|
||||
}
|
||||
|
||||
li .destroy:after {
|
||||
content: '\274c';
|
||||
}
|
||||
|
||||
li:hover .destroy {
|
||||
display: block;
|
||||
}
|
||||
63
R4.01_R4.A.10/td_tp/tp5/src/ex1/todo.riot
Normal file
@@ -0,0 +1,63 @@
|
||||
<todo>
|
||||
<h3>{ props.title }</h3>
|
||||
|
||||
<ul>
|
||||
<li each={ todo in state.todos } key = {todo.id}>
|
||||
<label class={ todo.done ? 'completed' : '' }>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked = { todo.done }
|
||||
onclick = { () => toggle(todo) } />
|
||||
{ todo.title }
|
||||
</label>
|
||||
<span class="destroy" onclick={() => remove(todo)}></span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<form onsubmit={ add }>
|
||||
<input oninput={ edit } />
|
||||
<button disabled={ !state.text }>
|
||||
Add #{ state.todos.length + 1 }
|
||||
</button>
|
||||
<button onclick={ clear }>Clear done</button>
|
||||
<ul class="filters">
|
||||
<li><span>0 todos left</span> </li>
|
||||
<li> <a href="#"> All</a></li>
|
||||
<li> <a href="#">Active</a></li>
|
||||
<li> <a href="#">Done</a></li>
|
||||
</ul>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
onBeforeMount(props, state) {
|
||||
// initial state
|
||||
this.state = {
|
||||
todos: props.todos,
|
||||
text: '',
|
||||
}
|
||||
},
|
||||
|
||||
edit(e) {
|
||||
// update only the text state
|
||||
this.update({
|
||||
text: e.target.value
|
||||
})
|
||||
},
|
||||
clear(e) {
|
||||
// TODO
|
||||
|
||||
},
|
||||
add(e) {
|
||||
// TODO
|
||||
},
|
||||
toggle(todo) {
|
||||
todo.done = !todo.done
|
||||
this.update()
|
||||
},
|
||||
remove(todo){
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</todo>
|
||||
18
R4.01_R4.A.10/td_tp/tp5/src/ex2/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Riot4 Radar Chart
|
||||
|
||||
## Usage
|
||||
|
||||
First of all, plz execute this commands.
|
||||
|
||||
```cmd
|
||||
$ cd ../
|
||||
$ yarn install
|
||||
$ yarn radar
|
||||
```
|
||||
|
||||
Perhaps the browser will open automatically and the application should start.
|
||||
|
||||
URL: [http://127.0.0.1:8080/](http://127.0.0.1:8080/)
|
||||
|
||||
## Demo
|
||||
demo: [CodeSandbox](https://codesandbox.io/embed/riot4-template-0f6i0)
|
||||
62
R4.01_R4.A.10/td_tp/tp5/src/ex2/app.riot
Normal file
@@ -0,0 +1,62 @@
|
||||
<app>
|
||||
<h2>{ props.title }</h2>
|
||||
<div id="radar">
|
||||
|
||||
<!-- Use the component -->
|
||||
|
||||
<svg
|
||||
is="polygraph"
|
||||
stats={ state.stats } />
|
||||
|
||||
|
||||
<!-- controls -->
|
||||
|
||||
<div>
|
||||
<div each={ (stat, index) in state.stats }>
|
||||
<label>{ stat.label }</label>
|
||||
<input type="range"
|
||||
onchange={ changeValue }
|
||||
min="0"
|
||||
max="100"
|
||||
data-index={ index }
|
||||
value={ stat.value }>
|
||||
<span>{ stat.value }</span>
|
||||
<button onclick={ remove } class="remove" value={ index }>X</button>
|
||||
</div>
|
||||
|
||||
<!-- add item -->
|
||||
<form id="add">
|
||||
<input type="text"
|
||||
name="newlabel"
|
||||
value={ state.newLabel }
|
||||
oninput={ inputStat }>
|
||||
<button onclick={ add }>Add a Stat</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
state: {
|
||||
newLabel: '',
|
||||
stats: []
|
||||
},
|
||||
onBeforeMount(props, state) {
|
||||
state.stats = props.stats
|
||||
},
|
||||
add(e) {
|
||||
// TODO
|
||||
},
|
||||
remove(stat) {
|
||||
// TODO
|
||||
},
|
||||
inputStat(e) {
|
||||
this.state.newLabel = e.target.value
|
||||
},
|
||||
changeValue(e) {
|
||||
this.state.stats[e.target.dataset.index].value = e.target.value
|
||||
this.update()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</app>
|
||||
31
R4.01_R4.A.10/td_tp/tp5/src/ex2/components/polygraph.riot
Normal file
@@ -0,0 +1,31 @@
|
||||
<polygraph>
|
||||
<g>
|
||||
<polygon points={ points() }></polygon>
|
||||
<circle cx="200" cy="200" r="160"></circle>
|
||||
<text
|
||||
each={ (stat, index) in state.stats }
|
||||
index={ index }
|
||||
stat={ stat }
|
||||
total={ state.stats.length }
|
||||
is="text" />
|
||||
</g>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
state: {
|
||||
stats: []
|
||||
},
|
||||
onBeforeMount(props, state) {
|
||||
state.stats = props.stats
|
||||
},
|
||||
// a computed property for the polygon's points
|
||||
points() {
|
||||
const total = this.state.stats.length
|
||||
return this.state.stats.map((stat, i) => {
|
||||
const point = valueToPoint(stat.value, i, total)
|
||||
return point.x + ',' + point.y
|
||||
}).join(' ')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</polygraph>
|
||||
25
R4.01_R4.A.10/td_tp/tp5/src/ex2/components/text.riot
Normal file
@@ -0,0 +1,25 @@
|
||||
<text x={ state.point.x } y={ state.point.y }>
|
||||
{ props.stat.label }
|
||||
|
||||
<script>
|
||||
export default {
|
||||
state: {
|
||||
point: ''
|
||||
},
|
||||
onBeforeMount(props, state) {
|
||||
state.point = valueToPoint(
|
||||
+props.stat.value + 10,
|
||||
props.index,
|
||||
props.total
|
||||
)
|
||||
},
|
||||
onBeforeUpdate(props, state) {
|
||||
state.point = valueToPoint(
|
||||
+props.stat.value + 10,
|
||||
props.index,
|
||||
props.total
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</text>
|
||||
13
R4.01_R4.A.10/td_tp/tp5/src/ex2/helper.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const valueToPoint = (value, index, total) => {
|
||||
const x = 0
|
||||
const y = -value * 1.6
|
||||
const angle = Math.PI * 2 / total * index
|
||||
const cos = Math.cos(angle)
|
||||
const sin = Math.sin(angle)
|
||||
const tx = x * cos - y * sin + 200
|
||||
const ty = x * sin + y * cos + 200
|
||||
return {
|
||||
x: tx,
|
||||
y: ty
|
||||
}
|
||||
}
|
||||
42
R4.01_R4.A.10/td_tp/tp5/src/ex2/index.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Riot.js Radar Chart</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/riot/10.0.1/riot+compiler.min.js"></script>
|
||||
<script src="helper.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- app -->
|
||||
|
||||
<app></app>
|
||||
|
||||
<!-- tags -->
|
||||
<script data-src="app.riot" type="riot"></script>
|
||||
<script data-src="components/polygraph.riot" type="riot"></script>
|
||||
<script data-src="components/text.riot" type="riot"></script>
|
||||
<script>
|
||||
riot
|
||||
.compile()
|
||||
.then(() => {
|
||||
riot.mount('app', {
|
||||
title: 'Riot.js Radar Chart',
|
||||
stats: [
|
||||
{ label: 'A', value: 100 },
|
||||
{ label: 'B', value: 100 },
|
||||
{ label: 'C', value: 100 },
|
||||
{ label: 'D', value: 100 },
|
||||
{ label: 'E', value: 100 },
|
||||
{ label: 'F', value: 100 }
|
||||
]
|
||||
})
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
46
R4.01_R4.A.10/td_tp/tp5/src/ex2/style.css
Normal file
@@ -0,0 +1,46 @@
|
||||
body {
|
||||
font-family: Helvetica Neue, Arial, sans-serif;
|
||||
}
|
||||
#radar{
|
||||
display : flex;
|
||||
}
|
||||
|
||||
svg {
|
||||
width:400px;
|
||||
height:400px;
|
||||
}
|
||||
polygon {
|
||||
fill: #ED1646;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
circle {
|
||||
fill: transparent;
|
||||
stroke: #999;
|
||||
}
|
||||
|
||||
text {
|
||||
font-family: Futura, Helvetica Neue, Arial, sans-serif;
|
||||
font-size: 10px;
|
||||
fill: #666;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#raw {
|
||||
/* position: absolute;
|
||||
top: 0;
|
||||
left: 300px;*/
|
||||
}
|
||||
|
||||
#add {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.remove {
|
||||
margin: 0 4px;
|
||||
}
|
||||
109
R4.01_R4.A.10/td_tp/tp6/README.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# TP : Une api rest pour la todo list.
|
||||
|
||||
Le but de l'exercice est d'écrire une api de données restful pour l'application todolist du
|
||||
[tp2](../tp2mvc) ou [tp5](../tp5).
|
||||
|
||||
|
||||
Les routes de notre api :
|
||||
|
||||
```
|
||||
get /todo/(:id)
|
||||
post /todo
|
||||
delete /todo/:id
|
||||
put /todo/:id
|
||||
```
|
||||
|
||||
- Pour routage et les entrées/sorties http, on utilise [flight php](https://flightphp.com/).
|
||||
- Pour les entrées/sorties avec la base données, je vous donne un son utilise l'orm [readbean php](https://www.redbeanphp.com/index.php).
|
||||
|
||||
Copiez le fichier `.htaccess` à la racine de vos sources pour activer la réécriture d'urls.
|
||||
|
||||
```apache
|
||||
Require method GET POST PUT DELETE OPTIONS
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [QSA,L]
|
||||
```
|
||||
|
||||
Votre api pour l'instant
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
require 'flight/Flight.php';
|
||||
require 'model/model.php';
|
||||
Flight::route('GET /todo(/@id)','getTodos');
|
||||
Flight::route('POST /todo','addTodo');
|
||||
Flight::route('DELETE /todo/@id','deleteTodo');
|
||||
Flight::route('PUT /todo/@id','updateTodo');
|
||||
|
||||
function deleteTodo($id)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
function updateTodo($id)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
function addTodo()
|
||||
{
|
||||
$todo = [
|
||||
"title" => Flight::request()->data->title ,
|
||||
"done" => Flight::request()->data->done
|
||||
];
|
||||
|
||||
$id = Todo::create($todo);
|
||||
Flight::response()->header("Location",Flight::request()->url.$id);
|
||||
$todo['id'] = $id;
|
||||
Flight::json($todo,201);
|
||||
|
||||
}
|
||||
|
||||
function getTodos($id = null)
|
||||
{
|
||||
$filter = Flight::request()->query->filter ?? "all";
|
||||
|
||||
if ($id === null){
|
||||
switch($filter){
|
||||
case "done":
|
||||
$todos = Todo::findCompleted();
|
||||
break;
|
||||
case "active":
|
||||
$todos = Todo::findUnCompleted();
|
||||
break;
|
||||
default:
|
||||
$todos = Todo::findAll();
|
||||
}
|
||||
Flight::json(
|
||||
[
|
||||
"results" => $todos
|
||||
]
|
||||
);
|
||||
|
||||
} else {
|
||||
$todo = Todo::find($id);
|
||||
if ($todo)
|
||||
Flight::json($todo);
|
||||
else
|
||||
Flight::halt(404);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Flight::start();
|
||||
```
|
||||
|
||||
Le modèle utilise PDO, en php. Il vous faudra créér une table avec les attributs nécessaires.
|
||||
|
||||
|
||||
|
||||
|
||||
1. Complétez le fichier index.php
|
||||
2. Testez votre api à la ligne de commandes en utilisant `curl`.
|
||||
3. Connectez votre application todolist avec l'api.
|
||||
4. Connectez votre api à l'application [todo](./src/todo-riot). Il faut
|
||||
complétez le fichier api.js. Prenez soin de modifier le fichier `htaccess`.
|
||||
911
R4.01_R4.A.10/td_tp/tp6/src/api_php/flight/Engine.php
Normal file
144
R4.01_R4.A.10/td_tp/tp6/src/api_php/flight/Flight.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use flight\Engine;
|
||||
use flight\net\Request;
|
||||
use flight\net\Response;
|
||||
use flight\net\Router;
|
||||
use flight\template\View;
|
||||
use flight\net\Route;
|
||||
|
||||
require_once __DIR__ . '/autoload.php';
|
||||
|
||||
/**
|
||||
* The Flight class is a static representation of the framework.
|
||||
*
|
||||
* @license MIT, http://flightphp.com/license
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
*
|
||||
* # Core methods
|
||||
* @method static void start() Starts the framework.
|
||||
* @method static void path(string $path) Adds a path for autoloading classes.
|
||||
* @method static void stop(?int $code = null) Stops the framework and sends a response.
|
||||
* @method static void halt(int $code = 200, string $message = '', bool $actuallyExit = true)
|
||||
* Stop the framework with an optional status code and message.
|
||||
* @method static void register(string $name, string $class, array $params = [], ?callable $callback = null)
|
||||
* Registers a class to a framework method.
|
||||
* @method static void unregister(string $methodName)
|
||||
* Unregisters a class to a framework method.
|
||||
* @method static void registerContainerHandler(callable|object $containerHandler) Registers a container handler.
|
||||
*
|
||||
* # Routing
|
||||
* @method static Route route(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
|
||||
* Maps a URL pattern to a callback with all applicable methods.
|
||||
* @method static void group(string $pattern, callable $callback, callable[] $group_middlewares = [])
|
||||
* Groups a set of routes together under a common prefix.
|
||||
* @method static Route post(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
|
||||
* Routes a POST URL to a callback function.
|
||||
* @method static Route put(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
|
||||
* Routes a PUT URL to a callback function.
|
||||
* @method static Route patch(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
|
||||
* Routes a PATCH URL to a callback function.
|
||||
* @method static Route delete(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
|
||||
* Routes a DELETE URL to a callback function.
|
||||
* @method static Router router() Returns Router instance.
|
||||
* @method static string getUrl(string $alias, array<string, mixed> $params = []) Gets a url from an alias
|
||||
*
|
||||
* @method static void map(string $name, callable $callback) Creates a custom framework method.
|
||||
*
|
||||
* @method static void before(string $name, Closure(array<int, mixed> &$params, string &$output): (void|false) $callback)
|
||||
* Adds a filter before a framework method.
|
||||
* @method static void after(string $name, Closure(array<int, mixed> &$params, string &$output): (void|false) $callback)
|
||||
* Adds a filter after a framework method.
|
||||
*
|
||||
* @method static void set(string|iterable<string, mixed> $key, mixed $value) Sets a variable.
|
||||
* @method static mixed get(?string $key) Gets a variable.
|
||||
* @method static bool has(string $key) Checks if a variable is set.
|
||||
* @method static void clear(?string $key = null) Clears a variable.
|
||||
*
|
||||
* # Views
|
||||
* @method static void render(string $file, ?array<string, mixed> $data = null, ?string $key = null)
|
||||
* Renders a template file.
|
||||
* @method static View view() Returns View instance.
|
||||
*
|
||||
* # Request-Response
|
||||
* @method static Request request() Returns Request instance.
|
||||
* @method static Response response() Returns Response instance.
|
||||
* @method static void redirect(string $url, int $code = 303) Redirects to another URL.
|
||||
* @method static void json(mixed $data, int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
|
||||
* Sends a JSON response.
|
||||
* @method static void jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
|
||||
* Sends a JSONP response.
|
||||
* @method static void error(Throwable $exception) Sends an HTTP 500 response.
|
||||
* @method static void notFound() Sends an HTTP 404 response.
|
||||
*
|
||||
* # HTTP caching
|
||||
* @method static void etag(string $id, ('strong'|'weak') $type = 'strong') Performs ETag HTTP caching.
|
||||
* @method static void lastModified(int $time) Performs last modified HTTP caching.
|
||||
*/
|
||||
class Flight
|
||||
{
|
||||
/** Framework engine. */
|
||||
private static Engine $engine;
|
||||
|
||||
/** Whether or not the app has been initialized. */
|
||||
private static bool $initialized = false;
|
||||
|
||||
/**
|
||||
* Don't allow object instantiation
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return void
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Forbid cloning the class
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return void
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles calls to static methods.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param array<int, mixed> $params Method parameters
|
||||
*
|
||||
* @return mixed Callback results
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function __callStatic(string $name, array $params)
|
||||
{
|
||||
return self::app()->{$name}(...$params);
|
||||
}
|
||||
|
||||
/** @return Engine Application instance */
|
||||
public static function app(): Engine
|
||||
{
|
||||
if (!self::$initialized) {
|
||||
require_once __DIR__ . '/autoload.php';
|
||||
|
||||
self::setEngine(new Engine());
|
||||
self::$initialized = true;
|
||||
}
|
||||
|
||||
return self::$engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the engine instance
|
||||
*
|
||||
* @param Engine $engine Vroom vroom!
|
||||
*/
|
||||
public static function setEngine(Engine $engine): void
|
||||
{
|
||||
self::$engine = $engine;
|
||||
}
|
||||
}
|
||||
10
R4.01_R4.A.10/td_tp/tp6/src/api_php/flight/autoload.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use flight\core\Loader;
|
||||
|
||||
require_once __DIR__ . '/Flight.php';
|
||||
require_once __DIR__ . '/core/Loader.php';
|
||||
|
||||
Loader::autoload(true, [dirname(__DIR__)]);
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace flight\commands;
|
||||
|
||||
use Nette\PhpGenerator\ClassType;
|
||||
use Nette\PhpGenerator\PhpFile;
|
||||
use Nette\PhpGenerator\PhpNamespace;
|
||||
|
||||
class ControllerCommand extends AbstractBaseCommand
|
||||
{
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param array<string,mixed> $config JSON config from .runway-config.json
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
parent::__construct('make:controller', 'Create a controller', $config);
|
||||
$this->argument('<controller>', 'The name of the controller to create (with or without the Controller suffix)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute(string $controller)
|
||||
{
|
||||
$io = $this->app()->io();
|
||||
if (isset($this->config['app_root']) === false) {
|
||||
$io->error('app_root not set in .runway-config.json', true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!preg_match('/Controller$/', $controller)) {
|
||||
$controller .= 'Controller';
|
||||
}
|
||||
|
||||
$controllerPath = getcwd() . DIRECTORY_SEPARATOR . $this->config['app_root'] . 'controllers' . DIRECTORY_SEPARATOR . $controller . '.php';
|
||||
if (file_exists($controllerPath) === true) {
|
||||
$io->error($controller . ' already exists.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_dir(dirname($controllerPath)) === false) {
|
||||
$io->info('Creating directory ' . dirname($controllerPath), true);
|
||||
mkdir(dirname($controllerPath), 0755, true);
|
||||
}
|
||||
|
||||
$file = new PhpFile();
|
||||
$file->setStrictTypes();
|
||||
|
||||
$namespace = new PhpNamespace('app\\controllers');
|
||||
$namespace->addUse('flight\\Engine');
|
||||
|
||||
$class = new ClassType($controller);
|
||||
$class->addProperty('app')
|
||||
->setVisibility('protected')
|
||||
->setType('flight\\Engine')
|
||||
->addComment('@var Engine');
|
||||
$method = $class->addMethod('__construct')
|
||||
->addComment('Constructor')
|
||||
->setVisibility('public')
|
||||
->setBody('$this->app = $app;');
|
||||
$method->addParameter('app')
|
||||
->setType('flight\\Engine');
|
||||
|
||||
$namespace->add($class);
|
||||
$file->addNamespace($namespace);
|
||||
|
||||
$this->persistClass($controller, $file);
|
||||
|
||||
$io->ok('Controller successfully created at ' . $controllerPath, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the class name to a file
|
||||
*
|
||||
* @param string $controllerName Name of the Controller
|
||||
* @param PhpFile $file Class Object from Nette\PhpGenerator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function persistClass(string $controllerName, PhpFile $file)
|
||||
{
|
||||
$printer = new \Nette\PhpGenerator\PsrPrinter();
|
||||
file_put_contents(getcwd() . DIRECTORY_SEPARATOR . $this->config['app_root'] . 'controllers' . DIRECTORY_SEPARATOR . $controllerName . '.php', $printer->printFile($file));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace flight\commands;
|
||||
|
||||
use Flight;
|
||||
use flight\net\Route;
|
||||
|
||||
/**
|
||||
* @property-read ?bool $get
|
||||
* @property-read ?bool $post
|
||||
* @property-read ?bool $delete
|
||||
* @property-read ?bool $put
|
||||
* @property-read ?bool $patch
|
||||
*/
|
||||
class RouteCommand extends AbstractBaseCommand
|
||||
{
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param array<string,mixed> $config JSON config from .runway-config.json
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
parent::__construct('routes', 'Gets all routes for an application', $config);
|
||||
|
||||
$this->option('--get', 'Only return GET requests');
|
||||
$this->option('--post', 'Only return POST requests');
|
||||
$this->option('--delete', 'Only return DELETE requests');
|
||||
$this->option('--put', 'Only return PUT requests');
|
||||
$this->option('--patch', 'Only return PATCH requests');
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$io = $this->app()->io();
|
||||
|
||||
if (isset($this->config['index_root']) === false) {
|
||||
$io->error('index_root not set in .runway-config.json', true);
|
||||
return;
|
||||
}
|
||||
|
||||
$io->bold('Routes', true);
|
||||
|
||||
$cwd = getcwd();
|
||||
|
||||
$index_root = $cwd . '/' . $this->config['index_root'];
|
||||
|
||||
// This makes it so the framework doesn't actually execute
|
||||
Flight::map('start', function () {
|
||||
return;
|
||||
});
|
||||
include($index_root);
|
||||
$routes = Flight::router()->getRoutes();
|
||||
$arrayOfRoutes = [];
|
||||
foreach ($routes as $route) {
|
||||
if ($this->shouldAddRoute($route) === true) {
|
||||
$middlewares = [];
|
||||
if (!empty($route->middleware)) {
|
||||
try {
|
||||
$middlewares = array_map(function ($middleware) {
|
||||
$middleware_class_name = explode("\\", get_class($middleware));
|
||||
return preg_match("/^class@anonymous/", end($middleware_class_name)) ? 'Anonymous' : end($middleware_class_name);
|
||||
}, $route->middleware);
|
||||
} catch (\TypeError $e) {
|
||||
$middlewares[] = 'Bad Middleware';
|
||||
} finally {
|
||||
if (is_string($route->middleware) === true) {
|
||||
$middlewares[] = $route->middleware;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$arrayOfRoutes[] = [
|
||||
'Pattern' => $route->pattern,
|
||||
'Methods' => implode(', ', $route->methods),
|
||||
'Alias' => $route->alias ?? '',
|
||||
'Streamed' => $route->is_streamed ? 'Yes' : 'No',
|
||||
'Middleware' => !empty($middlewares) ? implode(",", $middlewares) : '-'
|
||||
];
|
||||
}
|
||||
}
|
||||
$io->table($arrayOfRoutes, [
|
||||
'head' => 'boldGreen'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not to add the route based on the request
|
||||
*
|
||||
* @param Route $route Flight Route object
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function shouldAddRoute(Route $route)
|
||||
{
|
||||
$boolval = false;
|
||||
|
||||
$showAll = !$this->get && !$this->post && !$this->put && !$this->delete && !$this->patch;
|
||||
if ($showAll === true) {
|
||||
$boolval = true;
|
||||
} else {
|
||||
$methods = [ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH' ];
|
||||
foreach ($methods as $method) {
|
||||
$lowercaseMethod = strtolower($method);
|
||||
if (
|
||||
$this->{$lowercaseMethod} === true &&
|
||||
(
|
||||
$route->methods[0] === '*' ||
|
||||
in_array($method, $route->methods, true) === true
|
||||
)
|
||||
) {
|
||||
$boolval = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $boolval;
|
||||
}
|
||||
}
|
||||