Nous allons exploiter une base de données dans un environnement de développement plus concret sous la forme d'une application web PHP.
En root
apt update && apt install -y php php-mysql zip unzip
Configuration de la base de données
En usager standard
wget -O tweeties.zip https://bitbucket.org/jameshoffman/mariaphp-tweeties/get/master.zip
unzip tweeties.zip
cd jameshoffman-mariaphp-tweeties-<TAB>
cp .SECRETS.php SECRETS.php
# Inscrire les information de connexion à votre BD dans le fichier SECRETS.php
# Exécuter le fichier SQL de départ DATABASE.sql
php -S localhost:8080 webroot/index.php
# Parcourir l'app :)
Identifier les tables, relations et multiplicités de la base de données ci-dessous.
drop table if exists likes, tweeties, users;
--
-- USERS
--
create table users (
id int unsigned auto_increment primary key,
username varchar(50) not null unique,
pwd varchar(64) not null
);
insert into
users(username, pwd)
values
('alice', 'pwda'),
('bob', 'pwdb'),
('charlie', 'pwdc');
--
-- TWEETIES
--
create table tweeties(
id int unsigned auto_increment primary key,
text varchar(240) not null,
created_at datetime not null default now(),
updated_at datetime,
user_id int unsigned not null,
foreign key (user_id) references users(id)
on delete cascade
);
insert into tweeties (text, created_at, user_id)
values
('My first tweetie', date_add(now(), interval -36 hour), 1),
('Another tweetie', default, 1),
('Tweetie, here I am!!!1!11!!', date_add(now(), interval -49 hour), 2);
--
-- LIKES
--
create table likes (
user_id int unsigned not null,
tweety_id int unsigned not null,
primary key (user_id, tweety_id),
foreign key (user_id) references users(id)
on delete cascade,
foreign key (tweety_id) references tweeties(id)
on delete cascade
);
insert into
likes
values
(1, 1),
(2, 1),
(2, 2);
Modifier la base de données pour y intégrer les éléments ci-dessous.
DÉFI 🔥
Effectuer les changements sans détruire les données présentes dans la BD!
users
'^[^@]+@[^@]+\\.[^@]+$'
tweeties
users
'\\d+'
'[a-z]+'
'[A-Z]+'
SHA2(value, 256)
tweeties
NOW()
lors de la mise à jour+----+-----------------------------+---------+----------------+---------------------+-------------+
| id | text | user_id | username | last_edit | likes_count |
+----+-----------------------------+---------+----------------+---------------------+-------------+
| 1 | MY FIRST TWEETIE | 1 | alice@mail.com | 2023-11-17 13:29:53 | 2 |
| 2 | Another tweetie | 1 | alice@mail.com | 2023-11-17 13:29:48 | 1 |
| 3 | Tweetie, here I am!!!1!11!! | 2 | bob@mail.com | 2023-11-15 12:29:53 | 0 |
+----+-----------------------------+---------+----------------+---------------------+-------------+
drop table if exists likes, tweeties, users;
--
-- USERS
--
create table users (
id int unsigned auto_increment primary key,
username varchar(50) not null unique check(username regexp '^[^@]+@[^@]+\\.[^@]+$'), -- check
pwd varchar(64) not null
);
--
-- TRIGGER
--
-- Pour pouvoir faire des ; dans le trigger
delimiter &&
-- Comment éviter de répéter le même code dans 2 triggers?
-- https://mariadb.com/kb/en/stored-procedures/
--
--
--
-- create or replace procedure validate (in pwd varchar(100))
-- begin
-- if( pwd not regexp '\\d+' ) then
-- signal sqlstate '45000' set message_text = 'password must contain at least 1 number.';
-- end if;
--
-- if( binary pwd not regexp '[a-z]+' ) then
-- signal sqlstate '45000' set message_text = 'password must contain at least 1 lower case letter.';
-- end if;
--
-- if( binary pwd not regexp '[a-z]+' ) then
-- signal sqlstate '45000' set message_text = 'password must contain at least 1 upper case letter.';
-- end if;
--
-- if( char_length(pwd) < 8) then
-- signal sqlstate '45000' set message_text = 'password must contain at least 8 characters long.';
-- end if;
-- end&&
--
--
--
-- Puis dans le trigger
--
-- call validate(new.pwd);
create or replace trigger users_insert_hash_password before insert on users for each row
begin
if( new.pwd not regexp '\\d+' ) then
signal sqlstate '45000' set message_text = 'password must contain at least 1 number.';
end if;
if( binary new.pwd not regexp '[a-z]+' ) then
signal sqlstate '45000' set message_text = 'password must contain at least 1 lower case letter.';
end if;
if( binary new.pwd not regexp '[a-z]+' ) then
signal sqlstate '45000' set message_text = 'password must contain at least 1 upper case letter.';
end if;
if( char_length(new.pwd) < 8) then
signal sqlstate '45000' set message_text = 'password must contain at least 8 characters long.';
end if;
set new.pwd = sha2(new.pwd, 256);
end&&
create or replace trigger users_update_hash_password before update on users for each row
begin
if( new.pwd not regexp '\\d+' ) then
signal sqlstate '45000' set message_text = 'password must contain at least 1 number.';
end if;
if( binary new.pwd not regexp '[a-z]+' ) then
signal sqlstate '45000' set message_text = 'password must contain at least 1 lower case letter.';
end if;
if( binary new.pwd not regexp '[a-z]+' ) then
signal sqlstate '45000' set message_text = 'password must contain at least 1 upper case letter.';
end if;
if( char_length(new.pwd) < 8) then
signal sqlstate '45000' set message_text = 'password must contain at least 8 characters long.';
end if;
set new.pwd = sha2(new.pwd, 256);
end&&
delimiter ;
insert into
users(username, pwd)
values
('alice@mail.com', 'pwdpwdA1'),
('bob@mail.com', 'pwdpwdB2'),
('charlie@mail.com', 'pwdpwdC3');
--
-- TWEETIES
--
create table tweeties(
id int unsigned auto_increment primary key,
text varchar(240) not null check(trim(text) != ''), -- CHECK
created_at datetime not null default now(),
updated_at datetime,
user_id int unsigned not null,
-- CHECK
constraint created_less_than_updated check(ifnull(created_at < updated_at, true)),
foreign key (user_id) references users(id)
on delete cascade
);
delimiter &&
create or replace trigger tweeties_create_created_at before insert on tweeties for each row
begin
set new.created_at = now();
end&&
create or replace trigger tweeties_update_updated_at before update on tweeties for each row
begin
set new.updated_at = now();
end&&
delimiter ;
insert into tweeties (text, created_at, user_id)
values
('My first tweetie', date_add(now(), interval -36 hour), 1),
("Another tweetie", date_add(now(), interval -5 second), 1),
("Tweetie, here I am!!!1!11!!", date_add(now(), interval -49 hour), 2);
--
-- LIKES
--
create table likes (
user_id int unsigned not null,
tweety_id int unsigned not null,
primary key (user_id, tweety_id),
foreign key (user_id) references users(id)
on delete cascade,
foreign key (tweety_id) references tweeties(id)
on delete cascade
);
insert into
likes
values
(1, 1),
(2, 1),
(2, 2);
--
-- VIEW
--
-- Pourrait être réutilisé dans la liste globale des tweeties ET le profil d'un tweetor
create or replace view tweets as
select
tweeties.id,
tweeties.text,
tweeties.user_id,
username,
coalesce(updated_at, created_at) as `last_edit`,
count(likes.user_id) as `likes_count`
from tweeties
inner join users on user_id = users.id
left join likes on tweeties.id = likes.tweety_id
group by tweeties.id
order by last_edit desc;
--
-- TESTS
--
select * from users;
-- pas email
-- insert into users values (default, 'a', 'pwdpwd123');
-- pas password valide
-- insert into users values (default, 'a@a.a', 'pwd');
-- text vide
-- insert into tweeties(text, user_id) values (' ', 1);
-- password update trigger
-- password invalide
-- update users set pwd = 'non' where id = 1;
-- password hash apres update
update
users
set
pwd = 'pwdaAa111'
where id = 1;
select * from users where id = 1;
-- tweeties
select * from tweets;
-- created_at trigger
-- insert into tweeties(text, created_at, user_id) values ('mauvaise created at', date_add(now(), interval 36 year), 1);
-- updated_at trigger
update
tweeties
set
text = upper(text)
where id = 1;
-- invalid created_at
-- update tweeties set created_at = now() where id = 1;
select * from tweets;
select * from tweets where user_id = 1;