WordPress

Security

Installer un plugin de sécurité

Changer l’URL de connexion

  • WPS Hide Login par WPServeur, NicolasKulka, tabrisrp

Limiter le nombre de tentatives de connexion

  • WPS Limit Login par WPServeur, NicolasKulka, tabrisrp
  • Limit Login Attempts Reloaded par WPChef

Désactiver l’éditeur

Ajouter dans wp-config.php :

define(‘DISALLOW_FILE_EDIT’, true);

Sécuriser le header dans .htaccess

Ajouter dans .htaccess :

Options -Indexes

# Security Headers
<IfModule mod_headers.c>
    Header set X-XSS-Protection "1; mode=block"
	Header set X-Frame-Options "SAMEORIGIN"
	Header set X-Content-Type-Options "nosniff"
	Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
    Header set Referrer-Policy "strict-origin-when-cross-origin"
    Header set Feature-Policy "geolocation 'self'; vibrate 'none'"
    Header set Content-Security-Policy "default-src 'self' *.paypal.com *.paypalobjects.com; script-src 'self' 'unsafe-inline' *.wp.com; style-src 'self' 'unsafe-inline' *.jquery.com; media-src 'self' *.issuu.com *.youtube.com *.vimeo.com *soundcloud.com *bandcamp.com; child-src 'self' *.issuu.com *.youtube.com *.vimeo.com *soundcloud.com *bandcamp.com; form-action 'self' 'unsafe-inline' 'unsafe-eval'"
</IfModule>

Peut induire des problèmes au click de checkbox ! En ce cas commenter la ligne Header set Content-Security-Policy... en mettant un # en début de ligne.

Vérifier le niveau de sécurité du site avec securityheaders.com.

source : medium.com/@AmDee_Elyssa/10-wordpress-tips-to-make-your-website-secure

Tips

10 useful tips for WordPress dashboard customization

Keep logged in on WordPress for a longer period

Add the code to functions.php and adjust the amount of seconds if needed ( line 3).

add_filter( 'auth_cookie_expiration', 'stay_logged_in_for_1_year' );
function stay_logged_in_for_1_year( $expire ) {
  return 31556926; // 1 year in seconds
}

Remove dashboard menus

Paste the code into functions.php file from your theme directory. The following example will remove all menus named in the $restricted array.

function remove_menus () {
    global $menu;
    $restricted = array(__('Dashboard'), __('Posts'), __('Media'), __('Links'), __('Pages'), __('Appearance'), __('Tools'), __('Users'), __('Settings'), __('Comments'), __('Plugins'));
    end ($menu);
    while (prev($menu)){
            $value = explode(' ',$menu[key($menu)][0]);
            if(in_array($value[0] != NULL?$value[0]:"" , $restricted)){unset($menu[key($menu)]);}
    }
}
add_action('admin_menu', 'remove_menus');

Require a featured image before you can publish posts

If your blog layout is set to display a featured image, it can be useful to prevent post publishing th post without the features image set.

This code has to be pasted into your functions.php file.

add_action('save_post', 'wpds_check_thumbnail');
add_action('admin_notices', 'wpds_thumbnail_error');

function wpds_check_thumbnail( $post_id ) {
  // change to any custom post type 
  if( get_post_type($post_id) != 'post' )
      return;

  if ( ! has_post_thumbnail( $post_id ) ) {
    // set a transient to show the users an admin message
    set_transient( "has_post_thumbnail", "no" );
    // unhook this function so it doesn't loop infinitely
    remove_action('save_post', 'wpds_check_thumbnail');
    // update the post set it to draft
    wp_update_post(array('ID' => $post_id, 'post_status' => 'draft'));

    add_action('save_post', 'wpds_check_thumbnail');
  } else {
    delete_transient( "has_post_thumbnail" );
  }
}

function wpds_thumbnail_error() {
  // check if the transient is set, and display the error message
  if ( get_transient( "has_post_thumbnail" ) == "no" ) {
    echo "<div id='message' class='error'><p><strong>You must add a Featured Image before publishing this. Don't panic, your post is saved.</strong></p></div>";
    delete_transient( "has_post_thumbnail" );
  }
}

Add custom login logo

function my_custom_login_logo() {
    echo '<style type="text/css">
        h1 a { background-image:url('.get_bloginfo('template_directory').'/images/custom-login-logo.gif) !important; }
    </style>';
}

add_action('login_head', 'my_custom_login_logo');

Place a custom logo in dashboard

copy the code below and paste it to your functions.php file

add_action('admin_head', 'my_custom_logo');

function my_custom_logo() {
   echo '<style type="text/css">
         #header-logo { background-image: url('.get_bloginfo('template_directory').'/images/custom-logo.gif) !important; }</style>';
}

Remove dashboard widgets

Dashboard widgets can be pretty useful. For example, a WordPress developers can display your Google Analytics stats. Though, sometimes you don’t need it, or at least don’t need some of them.

The code below will allow you to remove WordPress’ dashboard widgets once you paste it in your functions.php file.

function example_remove_dashboard_widgets() {
        // Globalize the metaboxes array, this holds all the widgets for wp-admin
        global $wp_meta_boxes;

        // Remove the incomming links widget
        unset($wp_meta_boxes['dashboard']['normal']['core']['dashboard_incoming_links']);       

        // Remove right now
        unset($wp_meta_boxes['dashboard']['normal']['core']['dashboard_right_now']);
        unset($wp_meta_boxes['dashboard']['side']['core']['dashboard_primary']);
        unset($wp_meta_boxes['dashboard']['side']['core']['dashboard_secondary']);
}

// Hook into the 'wp_dashboard_setup' action to register our function
add_action('wp_dashboard_setup', 'example_remove_dashboard_widgets' );

Remove dashboard widgets for specific user roles

function customize_meta_boxes() {
     //retrieve current user info
     global $current_user;
     get_currentuserinfo();

     //if current user level is less than 3, remove the postcustom meta box
     if ($current_user->user_level < 3)
          remove_meta_box('postcustom','post','normal');
}

add_action('admin_init','customize_meta_boxes');

Add custom widgets to WordPress dashboard

function example_dashboard_widget_function() {
        // Display whatever it is you want to show
        echo "Hello World, I'm a great Dashboard Widget";
} 

// Create the function use in the action hook
function example_add_dashboard_widgets() {
        wp_add_dashboard_widget('example_dashboard_widget', 'Example Dashboard Widget', 'example_dashboard_widget_function');
}
// Hook into the 'wp_dashboard_setup' action to register our other functions
add_action('wp_dashboard_setup', 'example_add_dashboard_widgets' );

Change WordPress dashboard colors

function custom_colors() {
   echo '<style type="text/css">#wphead{background:#069}</style>';
}

add_action('admin_head', 'custom_colors');

Provide custom help messages

function my_admin_help($text, $screen) {
    // Check we're only on my Settings page
    if (strcmp($screen, MY_PAGEHOOK) == 0 ) {

        $text = 'Here is some very useful information to help you use this plugin...';
        return $text;
    }
    // Let the default WP Dashboard help stuff through on other Admin pages
    return $text;
}

add_action( 'contextual_help', 'my_admin_help' );

Reduce amount of post revisions

Post revisions are very useful, but they also clutter your database. In order to save space, you can consider limiting the amount of post revisions automatically saved by WordPress.

This code has to be pasted in your wp-config.php file, located at the root of your WordPress install.

define( 'WP_POST_REVISIONS', 3 );

Disable WordPress Login Hints

Paste the code below into your functions.php file to prevent login error messages to be displayed.

function no_wordpress_errors(){
  return 'GET OFF MY LAWN !! RIGHT NOW !!';
}
add_filter( 'login_errors', 'no_wordpress_errors' );

Docker

Commandes

Créer une image

OpenClassRooms : Créez votre premier Dockerfile

  • FROM définit l’image source
  • RUN exécute des commandes dans le conteneur
  • ADD ajouter des fichiers au conteneur
  • WORKDIR définit le répertoire de travail
  • EXPOSE définit les ports d’écoute par défaut (facultatif)
  • VOLUME définit les volumes utilisables (facultatif)
  • CMD définit la commande par défaut lors de l’exécution des conteneurs Docker

Docker Compose

Commandes

  • docker-compose up -d démarre l’ensemble des conteneurs en arrière-plan
  • docker-compose ps montre le status de l’ensemble d’une stack
  • docker-compose logs -f --tail 5 affiche les logs d’une stack
  • docker-compose stop arrête l’ensemble des services d’une stack
  • docker-compose down détruit l’ensemble des ressources d’une stack
  • docker-compose config valide la syntaxe du fichier docker-compose.yml

Arguments

  • image spécifie l’image source pour le conteneur
  • build spécifie le Dockerfile source pour créer l’image du conteneur
  • volume spécifie les points de montage entre le système hôte et les conteneurs
  • restart définit le comportement du conteneur en cas d’arrêt du processus
  • environment définit les variables d’environnement
  • depends_on dit que le conteneur dépend d’un autre conteneur
  • ports définit les ports disponibles entre la machine host et le conteneur

Dev

SQL

Base de données

Jeux de caractères

À chaque connexion à MySQL, exécutez donc la commande suivante:

SET NAMES 'utf8';

ou ajoutez une option lors de la connexion:

mysql -u utilisateur -p --default-character-set=utf8

Création d’une base de données

CREATE DATABASE elevage CHARACTER SET 'utf8';

Utiliser une base de données

USE nom_de_la_BDD;

ou

mysql -u nom_utilisateur -p nom_de_la_BDD --default-character-set=utf8

Suppression d’une base de données

DROP DATABASE IF EXISTS elevage;

Exécuter un fichier externe

SOURCE Users\taguan\dossierX\monFichier.sql;

sous windows:

SOURCE C:/"Document and Settings"/dossierX/monFichier.sql;

ou

\. monFichier.sql;

ou

mysql nom_base < chemin_fichier_de_sauvegarde.sql

Insérer des données à partir d’un fichier formaté

LOAD DATA [LOCAL] INFILE 'nom_fichier'
INTO TABLE nom_table
[FIELDS
    [TERMINATED BY '\t']
    [ENCLOSED BY '']
    [ESCAPED BY '\\' ]
]
[LINES 
    [STARTING BY '']    
    [TERMINATED BY '\n']
]
[IGNORE nombre LINES]
[(nom_colonne,...)];

Exemple CSV:

nom;prenom;date_naissance
Charles;Myeur;1994-12-30
Bruno;Debor;1978-05-12
Mireille;Franelli;1990-08-23
LOAD DATA LOCAL INFILE 'personne.csv'
INTO TABLE Personne
FIELDS TERMINATED BY ';'
LINES TERMINATED BY '\n' -- ou '\r\n' selon l'ordinateur et le programme utilisés pour créer le fichier
IGNORE 1 LINES
(nom,prenom,date_naissance);

Sauvegarde d’une base de données

mysqldump -u user -p --opt nom_de_la_base > sauvegarde.sql
  • mysqldump : client permettant de sauvegarder les bases.
  • --opt : option de mysqldump qui lance la commande avec une série de paramètres qui font que la commande s’effectue très rapidement.
  • nom_de_la_base : nom de la base que l’on veut sauvegarder.
  • > sauvegarde.sql : le signe > indique que l’on va donner la destination de ce qui va être généré par la commande sauvegarde.sql. Il s’agit du nom du fichier qui contiendra la sauvegarde de notre base.

La base de données est donc sauvegardée. Notez que la commande pour créer la base elle-même n’est pas sauvée. Donc si vous effacez votre base par mégarde, il vous faut d’abord recréer la base de données (avec CREATE DATABASE nom_base;), puis exécuter la commande suivante:

mysql nom_base < chemin_fichier_de_sauvegarde.sql

Table

Moteurs de table

MyISAM

C’est le moteur par défaut. Les commandes d’insertion et sélection de données sont particulièrement rapides sur les tables utilisant ce moteur. Cependant, il ne gère pas certaines fonctionnalités importantes comme les clés étrangères, qui permettent de vérifier l’intégrité d’une référence d’une table à une autre table ou les transactions, qui permettent de réaliser des séries de modifications « en bloc », ou au contraire d’annuler ces modifications.

InnoDB

Plus lent et plus gourmand en ressources que MyISAM, ce moteur gère les clés étrangères et les transactions.
De plus, en cas de crash du serveur, il possède un système de récupération automatique des données.

Préciser un moteur lors de la création de la table:

ENGINE = INNODB;

Création d’une table

CREATE TABLE Animal (
    id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    espece VARCHAR(40) NOT NULL,
    sexe CHAR(1),
    date_naissance DATETIME NOT NULL,
    nom VARCHAR(30),
    commentaires TEXT,
    PRIMARY KEY (id)
)
ENGINE=INNODB;

Vérification d’une table

SHOW TABLES;      -- liste les tables de la base de données
DESCRIBE Animal;  -- liste les colonnes de la table avec leurs caractéristiques

Suppression d’une table

DROP TABLE Animal;

Modification d’une table

ALTER TABLE nom_table ADD ... -- permet d'ajouter quelque chose (une colonne par exemple)
ALTER TABLE nom_table DROP ... -- permet de retirer quelque chose 
ALTER TABLE nom_table CHANGE ...
ALTER TABLE nom_table MODIFY ... -- permettent de modifier une colonne

Index d’une table

Lors de la création d’une table

CREATE TABLE Animal (
    id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    espece VARCHAR(40) NOT NULL,
    sexe CHAR(1),
    date_naissance DATETIME NOT NULL,
    nom VARCHAR(30),
    commentaires TEXT,
    PRIMARY KEY (id),
    INDEX ind_date_naissance (date_naissance),  -- index sur la date de naissance
    INDEX ind_nom (nom(10))                     -- index sur le nom (le chiffre entre parenthèses étant le nombre de caractères pris en compte)
)
ENGINE=INNODB;
CREATE TABLE nom_table (
    colonne1 INT NOT NULL,   
    colonne2 VARCHAR(40), 
    colonne3 TEXT,
    UNIQUE [INDEX] ind_uni_col2 (colonne2),     -- Crée un index UNIQUE sur la colonne2, INDEX est facultatif
    FULLTEXT [INDEX] ind_full_col3 (colonne3)   -- Crée un index FULLTEXT sur la colonne3, INDEX est facultatif
)
ENGINE=MyISAM;

Après création d’une table

ALTER TABLE nom_table
ADD INDEX [nom_index] (colonne_index [, colonne2_index ...]); --Ajout d'un index simple

ALTER TABLE nom_table
ADD UNIQUE [nom_index] (colonne_index [, colonne2_index ...]); --Ajout d'un index UNIQUE

ALTER TABLE nom_table
ADD FULLTEXT [nom_index] (colonne_index [, colonne2_index ...]); --Ajout d'un index FULLTEXT
CREATE INDEX nom_index
ON nom_table (colonne_index [, colonne2_index ...]);  -- Crée un index simple

CREATE UNIQUE INDEX nom_index
ON nom_table (colonne_index [, colonne2_index ...]);  -- Crée un index UNIQUE

CREATE FULLTEXT INDEX nom_index
ON nom_table (colonne_index [, colonne2_index ...]);  -- Crée un index FULLTEXT

Avec contrainte

CREATE TABLE nom_table (
    colonne1 INT NOT NULL,   
    colonne2 VARCHAR(40), 
    colonne3 TEXT,
    CONSTRAINT [symbole_contrainte] UNIQUE [INDEX] ind_uni_col2 (colonne2)
);

ALTER TABLE nom_table
ADD CONSTRAINT [symbole_contrainte] UNIQUE ind_uni_col2 (colonne2);

Suppression d’un index

ALTER TABLE nom_table 
DROP INDEX nom_index;

Recherche avec FULLTEXT

Lorsque MySQL compare la chaîne de caractères que vous lui avez donnée et les valeurs dans votre table, il ne tient pas compte de tous les mots qu’il rencontre. Les règles sont les suivantes :

  • les mots rencontrés dans au moins la moitié des lignes sont ignorés (règle des 50 %)
  • les mots trop courts (moins de quatre lettres) sont ignorés
  • les mots trop communs (en anglais, about, after, once, under, the…) ne sont également pas pris en compte
Recherche naturelle
SELECT *                               -- Vous mettez évidemment les colonnes que vous voulez.
FROM nom_table
WHERE MATCH(colonne1[, colonne2, ...]) -- La (ou les) colonne(s) dans laquelle (ou lesquelles) on veut faire la recherche (index FULLTEXT correspondant nécessaire).
AGAINST ('chaîne recherchée');         -- La chaîne de caractères recherchée, entre guillemets bien sûr.
-- Exemple :
SELECT *
FROM Livre
WHERE MATCH(titre, auteur)
AGAINST ('Jules Verne');

Recherche par pertinence:

SELECT *, MATCH(titre, auteur) AGAINST ('Jules Verne Lune')
FROM Livre;
Recherche avec booléens

La recherche avec booléens possède les caractéristiques suivantes :

  • elle ne tient pas compte de la règle des 50 % qui veut qu’un mot présent dans 50 % des lignes au moins soit ignoré
  • elle peut se faire sur une ou des colonnes sur lesquelles aucun index FULLTEXT n’est défini (ce sera cependant beaucoup plus lent que si un index est présent)
  • les résultats ne seront pas triés par pertinence par défaut
SELECT * 
FROM nom_table
WHERE MATCH(colonne) 
AGAINST('chaîne recherchée' IN BOOLEAN MODE); -- IN BOOLEAN MODE à l'intérieur des parenthèses !

La recherche avec booléens permet d’être à la fois plus précis et plus approximatif dans ses recherches.

  • Plus précis, car on peut exiger que certains mots se trouvent dans la ligne ou soient absents de la ligne pour la sélectionner. On peut même exiger la présence de groupes de mots, plutôt que de rechercher chaque mot séparément.
  • Plus approximatif, car on peut utiliser un astérisque * en fin de mot, pour préciser que le mot peut finir de n’importe quelle manière.

Pour exiger la présence ou l’absence de certains mots, on utilise les caractères + et -. Un mot précédé par + devra être présent dans la ligne et inversement, précédé par - il ne pourra pas être présent.

-- Recherche sur le titre, qui doit contenir "bonheur", mais ne peut pas contenir "ogres"
SELECT * 
FROM Livre
WHERE MATCH(titre)
AGAINST ('+bonheur -ogres' IN BOOLEAN MODE);

-- Recherche sur titre, qui doit contenir tout le groupe de mots entre guillemets doubles
SELECT * 
FROM Livre 
WHERE MATCH(titre) 
AGAINST ('"Terre à la Lune"' IN BOOLEAN MODE);

-- Recherche sur titre, sur tous les mots commençant par "petit"
SELECT * 
FROM Livre
WHERE MATCH(titre)
AGAINST ('petit*' IN BOOLEAN MODE);

-- Recherche sur titre et auteur, de tous les mots commençant par "d"
SELECT * 
FROM Livre
WHERE MATCH(titre, auteur)
AGAINST ('d*' IN BOOLEAN MODE);

-- Recherche sur titre, qui doit contenir un mot commençant par "petit", mais ne peut pas contenir le mot "prose"
SELECT * 
FROM Livre
WHERE MATCH(titre)
AGAINST ('+petit* -prose' IN BOOLEAN MODE); -- mix d'un astérisque avec les + et -
Recherche avec extension de requête

La recherche avec extension de requête se déroule en deux étapes:

  1. Une simple recherche naturelle est effectuée.
  2. Les résultats de cette recherche sont utilisés pour faire une seconde recherche naturelle.
SELECT *
FROM Livre
WHERE MATCH(titre, auteur)
AGAINST ('Daniel' WITH QUERY EXPANSION);

Requêtes

INSERT

INSERT INTO Animal (espece, sexe, date_naissance) 
    VALUES ('tortue', 'F', '2009-08-03 05:12:00');

Insertion multiple

INSERT INTO Animal (espece, sexe, date_naissance, nom) 
VALUES ('chien', 'F', '2008-12-06 05:18:00', 'Caroline'),
       ('chat', 'M', '2008-09-11 15:38:00', 'Bagherra'),
       ('tortue', NULL, '2010-08-23 05:18:00', NULL);

SELECT

SELECT *
FROM table
WHERE condition
GROUP BY expression
HAVING condition
{ UNION | INTERSECT | EXCEPT }
ORDER BY expression
LIMIT count
OFFSET start

DELETE

DELETE FROM nom_table WHERE critères;
DELETE FROM Animal WHERE nom = 'Zoulou';
DELETE FROM Animal;

UPDATE

UPDATE nom_table SET col1 = val1 [, col2 = val2, ...] [WHERE ...];
UPDATE Animal SET sexe='F', nom='Pataude' WHERE id=21;
UPDATE Animal SET commentaires='modification de toutes les lignes';

WHERE

/* La liste des avions (code et nom) triés par vendeur et par quantité en stock décroissants */
SELECT productCode, productName
FROM products
WHERE productLine = 'Planes'
ORDER BY productVendor DESC, quantityInStock DESC
/* RESULTAT ==> 12 lignes / 1900s Vintage Tri-Plane */

/* La liste des produits (code, nom, échelle et quantité) qui ont une échelle soit de 1:10, soit de 1:18 triés par quantité en stock décroissante */
SELECT productCode, productName, productScale, quantityInStock
FROM products
WHERE productScale IN ('1:10', '1:18')	/* équivalent à WHERE productScale = '1:10' OR productScale = '1:18' */
ORDER BY quantityInStock DESC
/* RESULTAT ==> 48 lignes / 1995 Honda Civic */

/* La liste des produits (nom, vendeur et prix de vente) qui sont vendus au moins 132$ triés par nom du produit */
SELECT productName, productVendor, MSRP
FROM products
WHERE MSRP >= 132
ORDER BY productName
/* RESULTAT ==> 24 lignes / 1903 Ford Model A */

/* La liste des produits (code, nom et prix d'achat) des produits achetés au moins 60$ au plus 90$ triés par prix d'achat */
SELECT productCode, productName, buyPrice
FROM products
WHERE buyPrice BETWEEN 60 AND 90	/* équivalent à WHERE buyPrice >= 60 AND buyPrice <= 90 */
ORDER BY buyPrice
/* RESULTAT ==> 34 lignes / 1937 Lincoln Berline */

LIKE

Deux jokers existent pour LIKE:

  • '%' : qui représente n’importe quelle chaîne de caractères, quelle que soit sa longueur (y compris une chaîne de longueur 0)
  • '_' : qui représente un seul caractère

Quelques exemples:

  • 'b%' cherchera toutes les chaînes de caractères commençant par ‘b’ (« brocoli », « bouli », « b »).
  • 'b_' cherchera toutes les chaînes de caractères contenant deux lettres dont la première est ‘b’ (« ba », « bf », « b8 »).
  • '%ch%ne' cherchera toutes les chaînes de caractères contenant ‘ch’ et finissant par ‘ne’ (« chne », « chine », « échine », « le pays le plus peuplé du monde est la Chine »).
  • 'p_rl_' cherchera toutes les chaînes de caractères commençant par un « p » suivi d’un caractère, puis de « rl » et enfin se terminant par un caractère (« parle », « perla », « perle »).
Sensibilité à la casse
SELECT * FROM Animal WHERE nom LIKE '%Lu%'; -- insensible à la casse

SELECT * FROM Animal WHERE nom LIKE BINARY '%Lu%'; -- sensible à la casse

WHERE…IN

SELECT * FROM Animal 
WHERE nom IN ('Moka', 'Bilba', 'Tortilla', 'Balou', 'Dana', 'Redbul', 'Gingko');

Colonnes calculées

/* La liste des motos (nom, vendeur, quantité et marge) triés par marge décroissante */
SELECT productName, productVendor, quantityInStock, (MSRP - buyPrice) AS margin
FROM products
WHERE productLine = 'Motorcycles'
ORDER BY margin DESC
/* RESULTAT ==> 13 lignes / 2003 Harley-Davidson Eagle Drag Bike */

/* La liste des commandes (numéro, date de commande, date d'expédition, écart en jours entre les deux dates et statut) qui sont en cours de traitement ou qui ont été expédiées et ont un écart de plus de 10j triés par écart décroissant puis par date de commande */
SELECT orderNumber, orderDate, shippedDate, (shippedDate - orderDate) AS processTime, status
FROM orders
WHERE status = 'In Process' OR (status = 'Shipped' AND (shippedDate - orderDate) > 10)
ORDER BY processTime DESC, orderDate
/* RESULTAT ==> 33 lignes / 10165 */

/*La liste des produits (nom et valeur du stock à la vente) des années 1960 */
SELECT productName, (quantityInStock * MSRP) AS stockValue
FROM products
WHERE productName LIKE '196%'
/* RESULTAT ==> 16 lignes / 1969 Harley Davidson Ultimate Chopper */

GROUP BY

/* Le prix moyen d'un produit vendu par chaque vendeur triés par prix moyen décroissant */
SELECT productVendor, AVG(MSRP) AS averagePrice
FROM products
GROUP BY productVendor
ORDER BY averagePrice DESC
/* RESULTAT ==> 13 lignes / Welly Diecast Productions / 113.9325 */

/* Le nombre de produits pour chaque ligne de produit */
SELECT productLine, COUNT(productCode)
FROM products
GROUP BY productLine
/* RESULTAT ==> 7 lignes / Classic Cars / 38 */

/* Le total du stock et le total de la valeur du stock à la vente de chaque ligne de produit pour les produits vendus plus de 100$ trié par total de la valeur du stock à la vente */
SELECT productLine, SUM(quantityInStock) AS totalStock, SUM(quantityInStock * MSRP) AS totalStockValue
FROM products
WHERE MSRP > 100
GROUP BY productLine
ORDER BY totalStockValue
/* RESULTAT ==> 7 lignes / Ships / 429177.74 */

/* La quantité du produit le plus en stock de chaque vendeur trié par vendeur */
SELECT productVendor, MAX(quantityInStock) AS maxInStock
FROM products
GROUP BY productVendor
ORDER BY productVendor
/* RESULTAT ==> 13 lignes / Autoart Studio Design / 9354 */

/* Le prix de l'avion qui coûte le moins cher à l'achat */
SELECT MIN(buyPrice) AS cheapestPricePlane
FROM products
WHERE productLine = 'Planes'
/* RESULTAT ==> 1 ligne / 29.34$ */

/* Le crédit des clients qui ont payé plus de 20000$ durant l'année 2004 trié par crédit décroissant */
SELECT customerNumber, SUM(amount) AS totalCredit
FROM payments
WHERE paymentDate BETWEEN '2004-01-01' AND '2004-12-31'
GROUP BY customerNumber
HAVING totalCredit > 20000
ORDER BY totalCredit DESC
/* RESULTAT ==> 69 lignes / 141 / 293 765.51 */

INNER JOIN

/* La liste des employés (nom, prénom et fonction) et des bureaux (adresse et ville) dans lequel ils travaillent */
SELECT lastName, firstName, jobTitle, addressLine1, addressLine2, city
FROM employees
INNER JOIN offices ON offices.officeCode = employees.officeCode
/* RESULTAT ==> 23 lignes / Diane Murphy */

/* La liste des clients français ou américains (nom du client, nom, prénom du contact et pays) et de leur commercial dédié (nom et prénom) triés par nom et prénom du contact */
SELECT customerName, contactLastName, contactFirstName, country, lastName, firstName
FROM customers
INNER JOIN employees ON employees.employeeNumber = customers.salesRepEmployeeNumber
WHERE country IN ('France', 'USA')
ORDER BY contactLastName, contactFirstName
/* RESULTAT ==> 48 lignes / Miguel Barajas */

/* La liste des lignes de commande (numéro de commande, code, nom et ligne de produit) et la remise appliquée aux voitures ou motos commandées triées par numéro de commande puis par remise décroissante */
SELECT orderNumber, orderdetails.productCode, productName, productLine, (MSRP - priceEach) AS discount
FROM orderdetails
INNER JOIN products ON products.productCode = orderdetails.productCode
WHERE productLine IN ('Classic Cars', 'Vintage Cars', 'Motorcycles')
ORDER BY orderNumber, discount DESC
/* RESULTAT ==> 2026 lignes / 34 */

Requêtes complexes

/* Le total des paiements effectués de chaque client (numéro, nom et pays) américain, allemand ou français de plus de 50000$ trié par pays puis par total des paiements décroissant */
SELECT customers.customerNumber, customerName, country, SUM(amount) AS totalPayment
FROM customers
INNER JOIN payments ON payments.customerNumber = customers.customerNumber
WHERE country IN ('France', 'Germany', 'USA')
GROUP BY customers.customerNumber, customerName, country
HAVING totalPayment > 50000
ORDER BY country, totalPayment DESC
/* RESULTAT ==> 38 lignes / 146 / 130305.35 */

/* Le montant total de chaque commande (numéro et date) des clients New-Yorkais (nom) trié par nom du client puis par date de commande */
SELECT customerName, orders.orderNumber, orderDate, SUM(quantityOrdered * priceEach) AS totalAmount
FROM customers
INNER JOIN orders ON orders.customerNumber = customers.customerNumber
INNER JOIN orderdetails ON orders.orderNumber = orderdetails.orderNumber
WHERE city = 'NYC'
GROUP BY city, customerName, orderNumber, orderDate
ORDER BY customerName, orderDate

/* RESULTAT ==> 16 lignes / Classic Legends / 10115 / 21665.98 */

Sous-requêtes

Sous-requête renvoyant une seule ligne

SELECT *
FROM nom_table1
WHERE [ROW](colonne1, colonne2) = ( -- le ROW n'est pas obligatoire
    SELECT colonneX, colonneY
    FROM nom_table2
    WHERE...);                      -- Condition qui ne retourne qu'UNE SEULE LIGNE

Cette requête va donc renvoyer toutes les lignes de la table1 dont la colonne1 = la colonneX de la ligne résultat de la sous-requête ; ET la colonne2 = la colonneY de la ligne résultat de la sous-requête.

Conditions avec IN et NOT IN

SELECT id, nom, espece_id
FROM Animal
WHERE espece_id IN (
    SELECT id 
    FROM Espece
    WHERE nom_courant IN ('Tortue d''Hermann', 'Perroquet amazone')
);

Conditions avec ANY, SOME et ALL

  • ANY : veut dire « au moins une des valeurs ».
  • SOME : est un synonyme de ANY.
  • ALL : signifie « toutes les valeurs ».
-- Sélectionne les lignes de la table Animal dont espece_id est inférieur à au moins une des valeurs sélectionnées dans la sous-requête
SELECT *
FROM Animal
WHERE espece_id < ANY (
    SELECT id
    FROM Espece
    WHERE nom_courant IN ('Tortue d''Hermann', 'Perroquet amazone')
);
-- Sélectionne les lignes de la table Animal dont espece_id est inférieur à toutes les valeurs sélectionnées dans la sous-requête
SELECT *
FROM Animal
WHERE espece_id < ALL (
    SELECT id
    FROM Espece
    WHERE nom_courant IN ('Tortue d''Hermann', 'Perroquet amazone')
);

Conditions avec EXISTS et NOT EXISTS

-- sélectionne toutes les races dont on ne possède aucun animal.
SELECT * FROM Race
WHERE NOT EXISTS (SELECT * FROM Animal WHERE Animal.race_id = Race.id);

Connexion

PHP

$db = null;

function openBDD() {
    global $db;
    try {
        $db = new PDO("mysql:host=localhost;dbname=Cinema;charset=utf8", "root", "password"); 
    }
    catch (Exception $e) {
        die ("error, not good : " . $e->getMessage());
    }
}

function closeBDD() {
    global $db;
    $db = null;
}

Requête

function descRealisator() {
    global $db;
    $sql = "SELECT * FROM directors ORDER BY birthDate";
    $req = $db->query($sql);
    $req->setFetchMode(PDO::FETCH_OBJ); // création d'objet
    
    $description = "";
    while ($data = $req->fetch()) {
        $description .= "<section>";
        $description .= "<h2>".$data->firstName." ".$data->lastName."</h2>";
        $description .= "<p>".$data->comment."</p>";
        $description .= "</section>";
    }
    echo $description;
}
Dev

JavaScript

ES6

Affectations déstructurées

var httpOptions = { timeout: 2000, isCache: true };
// ES5 :
var httpTimeout = httpOptions.timeout;
var httpCache = httpOptions.isCache;
// ES6 :
const { timeout: httpTimeout, isCache: httpCache } = httpOptions;
// or
const { timeout, isCache } = httpOptions;
// you now have a variable named 'timeout' and one named 'isCache' with correct values

avec des objets imbriqués :

const httpOptions = { timeout: 2000, cache: { age: 2 } };
// later
const { cache: { age } } = httpOptions;
// you now have a variable named 'age' with value 2

avec des tableaux :

const timeouts = [1000, 2000, 3000];
// later
const [shortTimeout, mediumTimeout] = timeouts;
// you now have a variable named 'shortTimeout' with value 1000
// and a variable named 'mediumTimeout' with value 2000

avec des fonctions :

function randomPonyInRace() {
	const pony = { name: 'Rainbow Dash' };
	const position = 2;
	// ...
	return { pony, position };
}
const { position, pony } = randomPonyInRace();
// ou
const { pony } = randomPonyInRace();

Paramètres optionnels et valeurs par défaut

function getPonies(size, page) {
	size = size || 10;
	page = page || 1;
	// ...
	server.get(size, page);
}

ou

function getPonies(size = 10, page = 1) {
	// ...
	server.get(size, page);
}

avec appel de fonction :

function getPonies(size = defaultSize(), page = size - 1) {
// if page is not provided, it will be set to the value
// of the size parameter minus one.
// ...
server.get(size, page);
}

avec une affectation déstructurée :

const { timeout = 1000 } = httpOptions;

Rest Operator

function addPonies(...ponies) {
	for (let pony of ponies) {
	poniesInRace.push(pony);
	}
}
const [winner, ...losers] = poniesInRace;
// assuming 'poniesInRace' is an array containing several ponies
// 'winner' will have the first pony,
// and 'losers' will be an array of the other ones
const ponyPrices = [12, 3, 4];
const minPrice = Math.min(...ponyPrices);

Classes

class Pony {
	get color() {
		console.log('get color');
		return this._color;
	}
	set color(newColor) {
		console.log(`set color ${newColor}`);
		this._color = newColor;
	}
	static defaultSpeed() {
		return 10;
	}
}
const pony = new Pony();
pony.color = 'red';
// 'set color red'
console.log(pony.color);
// 'get color'  (red')

Héritage

class Animal {
	speed() {
		return 10;
	}
}
class Pony extends Animal {
	speed() {
		return super.speed() + 10;
	}
}
const pony = new Pony();
console.log(pony.speed()); // 20, as Pony overrides the parent method
class Animal {
	constructor(speed) {
		this.speed = speed;
	}
}
class Pony extends Animal {
	constructor(speed, color) {
		super(speed);
		this.color = color;
	}
}
const pony = new Pony(20, 'blue');
console.log(pony.speed); // 20

Promises

getUser(login)
	.then(function (user) {
		return getRights(user);
	})
	.then(function (rights) {
		updateMenu(rights);
	})

Une promise a trois états :

  • pending (« en cours ») : quand la promise n’est pas réalisée
    par exemple quand l’appel serveur n’est pas encore terminé.
  • fulfilled (« réalisée ») : quand la promise s’est réalisée avec succès
    par exemple quand l’appel HTTP serveur a retourné un status 200-OK.
  • rejected (« rejetée ») : quand la promise a échoué
    par exemple si l’appel HTTP serveur a retourné un status 404-NotFound.
const getUser = function (login) {
	return new Promise(function (resolve, reject) {
		// async stuff, like fetching users from server, returning a response
		if (response.status === 200) {
			resolve(response.data);
		} else {
			reject('No user');
		}
	});
};

Une gestion d’erreur par promise :

getUser(login)
	.then(function (user) {
		return getRights(user);
	}, function (error) {
		console.log(error); // will be called if getUser fails
		return Promise.reject(error);
	})
	.then(function (rights) {
		return updateMenu(rights);
	}, function (error) {
		console.log(error); // will be called if getRights fails
		return Promise.reject(error);
	})

Une gestion d’erreur globale pour toute la chaîne :

getUser(login)
	.then(function (user) {
		return getRights(user);
	})
	.then(function (rights) {
		return updateMenu(rights);
	})
	.catch(function (error) {
		console.log(error); // will be called if getUser or getRights fails
	})

arrow functions

getUser(login)
	// ES5
	.then(function (user) {
		return getRights(user); // getRights is returning a promise
	})
	.then(function (rights) {
		return updateMenu(rights);
	})
	// ES6
	.then(user => getRights(user))
	.then(rights => updateMenu(rights))

Le return est implicite s’il n’y a pas de bloc : pas besoin d’écrire user ⇒ return getRights(user).

les arrow functions ont une particularité bien agréable que n’ont pas les fonctions normales : le this reste attaché lexicalement, ce qui signifie que ces arrow functions n’ont pas un nouveau this comme les fonctions normales :

var maxFinder = {
	max: 0,
	find: function (numbers) {
		// ES5
		numbers.forEach(
		function (element) {
			if (element > this.max) {
				this.max = element;
			}
		}, this); // on doit passer le this en second paramètre
		// ES6
		numbers.forEach(element => {
			if (element > this.max) {
				this.max = element;
			}
		});
	}
};
maxFinder.find([2, 3, 4]);
// log the result
console.log(maxFinder.max);

Collections

Map

const cedric = { id: 1, name: 'Cedric' };
const users = new Map();
users.set(cedric.id, cedric); // adds a user
console.log(users.has(cedric.id)); // true
console.log(users.size); // 1
users.delete(cedric.id); // removes the user

Set

const cedric = { id: 1, name: 'Cedric' };
const users = new Set();
users.add(cedric); // adds a user
console.log(users.has(cedric)); // true
console.log(users.size); // 1
users.delete(cedric); // removes the user

for…of

for (let user of users) {
	console.log(user.name);
}

Template de string

// ES5
const fullname = 'Miss ' + firstname + ' ' + lastname;
// ES6
const fullname = `Miss ${firstname} ${lastname}`;

Modules

Dans races_service.js :

export function bet(race, pony) { ... }
export function start(race) { ... }

Dans un autre fichier :

import { bet, start } from './races_service';
// later
bet(race, pony1);
start(race);
// import avec alias
import { start as startRace } from './races_service';
// tout importer
import * as racesService from './races_service';
// default :
// pony.js
export default class Pony {}
// races_service.js
import Pony from './pony';

Liens

Git

Git Helpers

Commands

Some useful commands :

  • git config credential.helper store : This command stores credentials indefinitely on disk for use by future Git programs. [Git Credential Store]
  • git branch | grep -v "develop" | xargs git branch -D : Remove all your local git branches except develop.
  • git last` : `git config --global alias.last 'log -1 HEAD' : See the last commit easily.

You can easily set up an alias for each command using git config. [Git Aliases]

Commits

Prefixes with emojis for commits :

  • boom: First Commit or MAJOR UPDATE
  • :bug: BUGFIX
  • :toilet: CLEANUP
  • :wrench: CONFIG
  • :green_book: DOC
  • :star2: FEATURE
  • :speech_balloon: LANGUAGE/TRANSLATION
  • :truck: MOVE folders, files
  • :zap: PERFORMANCE
  • :electric_plug: PLUG Front/Back
  • :sparkles: REFACTOR
  • :lock: SECURITY
  • :art: STYLE
  • :bookmark: TAG
  • :mushroom: UPGRADE
  • :construction: WIP

Ressources :

Articles :

Dev