[AngularJs] Faire une factory AngularJs pour PHP-MYSQL : un intérêt limité.

angularjslogo-php

Introduction


J’ai fait une factory AngularJs pour gérer le back end PHP-MYSQL, MYSQL étant une base de données relationnelle, et donc très structurée et normée, ce qui est extrêmement appréciable, parfois, face à noSql.

Comme pré supposé, l’intérêt est assez limité, ça fait perdre du temps et le code n’est pas beaucoup simplifié.

La factory centralise certes les données, mais d’un autre côté, il faut écrire une chose de plus… Cela sert pour faire les tests unitaires aussi.

Du coup , il est fortement possible que je continue à faire mes appels AJAX avec $http dans les controleurs, lorsqu’il s’agit d’un back end PHP (Mais pas dans le cas d’une app en noSql).

Le but est toujours le même : Faire du CRUD : CREER, LIRE, METTRE A JOUR, SUPPRIMER

Schéma : (Le service est ici une factory, c’est similaire dans ce cas)


file-page1

Le Code exemple : La factory ANGULARJS qui gère les Citernes de PompeStation, on voit que le code est agréable à regarder:

 

La Factory :

app.factory('citernesFactory',function($http){

	var factory = {};

	/* Lire toutes les citernes */
	factory.getCiternes = function(){
		return $http.get('crud.php?action=get_citernes')
	}

	/* Lire une seule citerne */
	factory.getCiterne = function(idBdd){
		return $http.post('crud.php?action=get_citerne',{'id':idBdd})
	}

	/* Insérer une citerne  */
	 factory.insertCiterne = function(citerne){
        return $http.post('crud.php?action=insert_citerne',citerne)
     }

	/* Supprimer une citerne */
	factory.supprimerCiterne = function(idBdd){
        return $http.post('crud.php?action=delete_citerne',{'id':idBdd})
    }

	/* Editer une citerne */
	factory.editerCiterne = function(citerne){
		return $http.post('crud.php?action=update_citerne',citerne)
	}

	return factory
})	 

Le back end CRUD en PHP qui ne fait que gérer Mysql

C’est le fichier crud.php, Il réceptionne les données provenant de la factory AngularJs et renvoie des objets JSON ou des tableaux d’objets JSON, ou rien (Petite note annexe : on peut remarquer les jointures SQL dans les requêtes select qui permettent d’obtenir le volume de carburant restant par citerne…) :


<?php


/* -------------------------------------------------BACK END EN PDO------------------------------------------  */


include('connexionConfig.php'); 
/* mysql_set_charset('utf-8'); */  /// très important
header( 'content-type: text/html; charset=utf-8' );  
error_reporting(E_ALL ^ E_NOTICE); // important pour ne pas afficher les notice PHP sans cela le script ne marche pas.
 
/**  Switch Case pour récupérer la l'action demandée par le controleur  Angular **/
  
switch($_GET['action'])  {

    
	case 'get_citernes' :	get_citernes();
    break;
	case 'get_citerne' :	get_citerne();
    break;
	case 'get_citernes_par_station' :	get_citernes_par_station();
    break;
	case 'insert_citerne' :	insert_citerne();
    break;
	case 'delete_citerne' :	delete_citerne();
    break;
	case 'update_citerne' :	update_citerne();
    break;

	
}


/* --------------------------------------------------CRUD  ------------------------------------------*/

function get_citernes() {
	try
	{
		$DB = connection();
		$data = $DB->query('SELECT citernes.*,stockcarburant.volume AS volumerestant,stations.nom AS nomstation FROM citernes INNER JOIN stockcarburant ON citernes.id = stockcarburant.idciterne INNER JOIN stations on citernes.idstation = stations.id');
		/* Convertit en JSON  */
		print_r(json_encode($data->fetchAll(PDO::FETCH_ASSOC)));
		/* ferme la connexion ? */
		$DB=null;
	}
	catch(PDOException $e)
	{
		file_put_contents('PDOErreurs.txt', $e->getMessage(), FILE_APPEND);
	}
}

function get_citernes_par_station() {
 /* Récupération des données POST */
	 $data = json_decode(file_get_contents("php://input"));
	try
	{
		$DB = connection();
		$data = $DB->query("SELECT citernes.*,citernes.id as idciterne FROM citernes INNER JOIN stations ON citernes.idstation = stations.id WHERE stations.id=".$data->id."");

		/* Convertit en JSON  */
		print_r(json_encode($data->fetchAll(PDO::FETCH_ASSOC)));
		/* ferme la connexion ? */
		$DB=null;
	}
	catch(PDOException $e)
	{
		file_put_contents('PDOErreurs.txt', $e->getMessage(), FILE_APPEND);
	}
}

function get_citerne() {
  /* Récupération des données POST */
	 $data = json_decode(file_get_contents("php://input"));
	/* echo($data->id); */
	try
	{
		$DB = connection();
		$sql = $DB->query("SELECT citernes.*,stockcarburant.volume AS volumerestant FROM citernes INNER JOIN stockcarburant ON citernes.id = stockcarburant.idciterne WHERE citernes.id=".$data->id."");

		/* $stmt = $DB->prepare($sql); */
		/* $sql->bindParam(':id',$data->id,PDO::PARAM_INT);  */
		/* echo($sql); */
		$sql->execute();
		$row = $sql->fetch();
	/* 	  echo($row);
		  var_dump($row); */
		print_r(json_encode($row));  

		$DB=null;
	}
	catch(PDOException $e)
	{
		file_put_contents('PDOErreurs.txt', $e->getMessage(), FILE_APPEND);
	}
} 

function insert_citerne() {
	/* Récupération des données POST */
	 $data = json_decode(file_get_contents("php://input")); 

	/* Insertion en Bdd avec PDO */
	try {
		$DB = connection();
		$req = $DB->prepare("INSERT INTO citernes VALUES (?,?,?,?,?)");
		$req->execute(array(null,$data->nom,$data->volumemax,$data->typecarburant,$data->idstation));
		$DB=null;
	} catch (PDOException $e) {
		file_put_contents('PDOErreurs.txt', $e->getMessage(), FILE_APPEND);
		die();
	}

	/* SETTING DU STOCK INITIAL  DE CARBURANT DE LA CITERNE A ZERO*/
	try {
		$DB = connection();
		$gid = $DB->prepare("SELECT MAX(id) as maxGroup FROM citernes");
		$gid->execute();
		$test = $gid->fetch(PDO::FETCH_ASSOC);
		echo($test);
		$req = $DB->prepare("INSERT INTO stockcarburant VALUES (?,?,?)");
		$req->execute(array(null,$test['maxGroup'],0));
		$DB=null;
	} catch (PDOException $e) {
		file_put_contents('PDOErreurs.txt', $e->getMessage(), FILE_APPEND);
		die();
	}

}

 function delete_citerne() {
	/* Récupération des données POST */
	 $data = json_decode(file_get_contents("php://input"));
	/* Delete en Bdd avec PDO */
	try {
		$DB = connection();
		$sql = "DELETE FROM citernes WHERE  id=:id";
		$stmt = $DB->prepare($sql);
		$stmt->bindParam(':id',$data->id,PDO::PARAM_INT);
		$stmt->execute();
		$DB=null;
	} catch (PDOException $e) {
		file_put_contents('PDOErreurs.txt', $e->getMessage(), FILE_APPEND);
		die();
	}
}

 function update_citerne() {
	$data = json_decode(file_get_contents("php://input"));

	/* Update en Bdd avec PDO (pour la sécurité ce serait mieux de spécifier le typage de vars pour le prepared statement)*/
	try {
		$DB = connection();
		$sql = "UPDATE citernes SET nom='".$data->nom."',volumemax='".$data->volumemax."',typecarburant='".$data->typecarburant."',idstation='".$data->idstation."' WHERE  id=".$data->id." ";
		$stmt = $DB->prepare($sql); // Est ce sur que cela fonctionne comme ça ?
		$stmt->execute();
		$DB=null;
	} catch (PDOException $e) {
		file_put_contents('PDOErreurs.txt', $e->getMessage(), FILE_APPEND);
		die();
	}
}

Le Controleur FRONT END ANGULARJS qui tourne avec la factory que j’ai appelé citernesFactory : On ne lance plus directement des appels AJAX $http, mais on lance des appels à la factory, qui elle, lance les appels AJAX



app.controller('citernesCtrl', function($scope,$http,notifier,stockeUtilisateur,citernesFactory) {	

/* INITIALISATION  */
$scope.citerne = {}; // Contient un seul 
$scope.citernes=[]; // Contient tous 
u = stockeUtilisateur.getUtilisateur(); // Get l utilisateur actuel
$scope.station = {};// pour le filtre

/* INIT DIVERS */
$scope.montreBtn1 = true; 
$scope.montreBtn4 = false;
$scope.selectedRow = null;  // set la ligne d choix à null
$scope.mr=false; //montre l objet ou pas

$scope.liste = function(){
	citernesFactory.getCiternes().success(function(data){
		$scope.citernes = data;
	});
}  
$scope.liste();

$http.get('crud.php?action=get_stations').success(function(data){
	$scope.stations = data;
}).error(function(data){ $scope.infos = " Pas de données ou pb de connexion"});


/* Clic sur le tableau */
$scope.edit		=function(idBdd,index){
	$scope.montreBtn1 = false; $scope.montreBtn2 = true;$scope.montreBtn3 = true;$scope.montreBtn4 = true; // Gestion des bouttons
	$scope.selectedRow = index; // Coloration du choix de la ligne
	citernesFactory.getCiterne(idBdd).success(function(data){
		$scope.citerne = data;
	});
	$scope.mr=true; //montre l objet ou pas
}

/* Clic sur bouton nouveau */
$scope.nouveau 	= function(){
	$scope.montreBtn1 = true;$scope.montreBtn2 = false;$scope.montreBtn3 = false;$scope.montreBtn4 = false;$scope.produit = {};
	$scope.citerne = {};
	notifier.notify('Entrez une nouvelle citerne');
	$scope.selectedRow = null; 
}

/* INSERT */
$scope.enregistrer = function(){
	
	var citerne = (angular.copy($scope.citerne));
	$scope.montreBtn1 = true; $scope.montreBtn2 = false;$scope.montreBtn3 = false;$scope.montreBtn4 = false;
	
	citernesFactory.insertCiterne(citerne).success(function(data){
		notifier.notify('Enregistré');
		$scope.liste();
	});
	$scope.citerne = {};	
}

/* DELETE */
$scope.supprimer = function(idBdd){
	if(u.role=='admin'){
		$scope.montreBtn1 = true; $scope.montreBtn2 = false;$scope.montreBtn3 = false;$scope.montreBtn4 = true;
		
		citernesFactory.supprimerCiterne(idBdd).success(function(data){
			notifier.notify('Enregistré');
			$scope.liste();
		});
		$scope.citerne = {};
	}
	else{
		notifier.notify('Vous devez être admin pour supprimer un élément');
	}
}

/* UPDATE */
$scope.appliquer = function(){
	var citerne = (angular.copy($scope.citerne));
	$scope.montreBtn1 = false; $scope.montreBtn2 = false;$scope.montreBtn3 = false;$scope.montreBtn4 = true;
	
	citernesFactory.editerCiterne(citerne).success(function(data){
			notifier.notify('Enregistré');
			$scope.liste();
	});
	$scope.citerne = {};
}

$scope.tauxRemplissage = function(citerne) {
   return (citerne.volumerestant*100/citerne.volumemax).toFixed(2);
};

/* FIN DU CONTROLEUR */
})

En comparant avec l’ancien controleur, sans factory , on se rends compte que créer la factory fait perdre du temps et que les appels AJAX directement dans le controleur, dans le cas d’un back end PHP, c’est pas si mal ! Certes, ce n’est pas très beau à regarder, mais c’est efficace:


app.controller('citernesCtrl', function($scope,$http,notifier,stockeUtilisateur) {	

/* INITIALISATION */
$scope.citerne = {}; // Contient un seul 
$scope.citernes=[]; // contient tous 
u = stockeUtilisateur.getUtilisateur();
$scope.station = {};//pour le filtre


/* INIT DIVERS */
$scope.montreBtn1 = true; 
$scope.montreBtn4 = false;
$scope.selectedRow = null;  // set la ligne d choix à null
$scope.mr=false; //montre l objet ou pas


$scope.liste = function(){
	$http.get('crud.php?action=get_citernes').success(function(data){
	$scope.citernes = data;
}).error(function(data){ $scope.infos = " Pas de données ou pb de connexion"});
}

$http.get('crud.php?action=get_stations').success(function(data){
	$scope.stations = data;
}).error(function(data){ $scope.infos = " Pas de données ou pb de connexion"});



$scope.liste();

/* Clic sur le tableau */
$scope.edit		=function(idBdd,index){
	$scope.montreBtn1 = false; $scope.montreBtn2 = true;$scope.montreBtn3 = true;$scope.montreBtn4 = true; // Gestion des bouttons
	$scope.selectedRow = index; // Coloration du choix de la ligne
	$http.post('crud.php?action=get_citerne',{'id':idBdd}).success(function(data){
		$scope.citerne = data;
	}).error(function(data){ $scope.infos = " Pas de données ou pb de connexion"});
	$scope.mr=true; //montre l objet ou pas
}

/* Clic sur bouton nouveau */
$scope.nouveau 	= function(){
	$scope.montreBtn1 = true;$scope.montreBtn2 = false;$scope.montreBtn3 = false;$scope.montreBtn4 = false;$scope.produit = {};
	$scope.citerne = {};
	notifier.notify('Entrez une nouvelle citerne');
	$scope.selectedRow = null; 
}

/* INSERT */
$scope.enregistrer = function(){
	
	var citerne = (angular.copy($scope.citerne));
	$scope.montreBtn1 = true; $scope.montreBtn2 = false;$scope.montreBtn3 = false;$scope.montreBtn4 = false;
	$http.post('crud.php?action=insert_citerne',citerne).success(function(data){
		notifier.notify('Enregistré');
		$scope.liste();
	}).error(function(data){ $scope.infos = " Pas de données ou pb de connexion"});
	$scope.citerne = {};
}

/* DELETE */
$scope.supprimer = function(idBdd){
	if(u.role=='admin'){
	/* 	$scope.citernes.splice(id,1);
		$scope.citerne = {}; */
		$scope.montreBtn1 = true; $scope.montreBtn2 = false;$scope.montreBtn3 = false;$scope.montreBtn4 = true;
		
		$http.post('crud.php?action=delete_citerne',{'id':idBdd}).success(function(data){
			$scope.liste();
		}).error(function(data){ $scope.infos = " Pas de données ou pb de connexion"});
		$scope.citerne = {};
	}
	else{
		notifier.notify('Vous devez être admin pour supprimer un élément');
	}
}

/* UPDATE */
$scope.appliquer = function(){
	
	$scope.montreBtn1 = false; $scope.montreBtn2 = false;$scope.montreBtn3 = false;$scope.montreBtn4 = true;
	$http.post('crud.php?action=update_citerne',$scope.citerne).success(function(data){
		$scope.liste();
	}).error(function(data){ $scope.infos = " Pas de données ou pb de connexion"});
	$scope.citerne = {};
}

 $scope.tauxRemplissage = function(citerne) {
   return (citerne.volumerestant*100/citerne.volumemax).toFixed(2);
};

/* FIN DU CONTROLEUR */
 
 

})

Et, juste pour info, le look de la vue HTML citernes.html, qui tourne donc en adéquation avec le controleur angularJs montré au dessus, les CSS sont du bootstrap 3.0 :

<div class="col-lg-4" >
	<div class="panel panel-default">
  
		<div class="panel-heading">
			<span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> Liste
		</div>
	  
		<div class="panel-body panel-nico" >
			<ui-select  theme="select2" ng-model="station.selected"   style="min-width: 100%;">
				<ui-select-match placeholder="Selectionnez la station..."> <img ng-src="{{$select.selected.image}}" class="imageX"/> {{$select.selected.nom}}</ui-select-match>
					<ui-select-choices repeat="station in stations | filter:{nom:$select.search}  "  >
						 <img ng-src="{{::station.image}}" class="imageX"/>
						 <span ng-bind-html="::station.nom" | highlight: $select.search" ></span>
					</ui-select-choices>
			</ui-select>
			<br>
			
			 <scrollable-table watch="citernes"> 
				<table  class="table table-bordered table-hover  "  >
					<thead>
					  <tr>
						<th></th>
						<!-- <th sortable-header col="nom">Id</th> -->
						<th >Nom</th>
						<!-- <th >Station</th> -->
						<!-- <th sortable-header col="volumemax">Volume Maximum</th>
						<th sortable-header col="volumerestant">Volume Restant</th> -->
						<th >Remplissage</th>
						<th  >Carburant</th>
						<!-- 	<th >idStation</th> -->
					  </tr>
					</thead>
					
					<tbody >
						<tr  ng-repeat="citerne in citernes | filter:station.selected.nom | orderBy:tauxRemplissage" ng-click="edit(citerne.id,$index)"  ng-class="{'selected':$index == selectedRow,'highlight': citerne.volumerestant < 500 ,'highlightred': citerne.volumerestant < 100 }">
							<td><img class="imageX" src="img-app/Oil_industry-16-512.png"></img></td>
							<!-- <td >{{::citerne.id}}</td> -->
							<td >{{::citerne.nom}}</td>
							<!-- <td >{{::citerne.nomstation}}</td> -->
							<!-- <td >{{::citerne.volumemax | currency : "Litres"}}</td>
							<td >{{::citerne.volumerestant | currency : "Litres"}}</td> -->
							<td ng-init="test1 = (citerne.volumerestant*100/citerne.volumemax).toFixed(2)">  {{(citerne.volumerestant*100/citerne.volumemax).toFixed(2)}} %<uib-progressbar value="test1"></uib-progressbar> </td>
							<td >{{::citerne.typecarburant}}</td>
							<!-- <td >{{::citerne.idstation}}</td> -->
						</tr>
					</tbody>
				</table>
			 </scrollable-table>
			<br>
		</div>
	  
	   <div class="panel-footer text-right">
		
		<button type="button" class="btn btn-info" ng-csv="citernes" filename="citernes.csv"> 
			<span class="glyphicon glyphicon-file" aria-hidden="true"></span> Export CSV
		</button>
		
		<button type="button" class="btn btn-danger"  data-toggle="collapse" data-target="#demo">
			<img class="imageX" src="img-app/filtre_318-41056.ico"></img> Filtres 
		</button>
		
			<div id="demo" class="row collapse  text-left">
				<div class="col-md-8"><br>
					<div class="input-group">
						<span class="input-group-addon">Station</span>
						<ui-select  theme="select2" ng-model="station.selected"   style="min-width: 100%;">
							<ui-select-match placeholder="Selectionnez ..."> <img ng-src="{{$select.selected.image}}" class="imageX"/> {{$select.selected.nom}}</ui-select-match>
								<ui-select-choices repeat="station in stations | filter:{nom:$select.search}  "  >
									 <img ng-src="{{::station.image}}" class="imageX"/>
									 <span ng-bind-html="::station.nom" | highlight: $select.search" ></span>
								</ui-select-choices>
						</ui-select>
					</div><br>
				</div>
			</div>
		</div>
	<!-- FIN DE PANEL DEFAUT -->
	</div>
<!--  FIND DE COLONNE TABLE -->
</div>
  
  
<div class="col-lg-8" ng-show="mr">
	<div class="panel panel-default">
		<!-- Default panel contents -->
		<div class="panel-heading"><img class="imageX" src="img-app/Oil_industry-16-512.png"></img> Citerne</div>
		<form name="myForm">
		<div class="panel-body panel-nico" >
			<div class="row">
			<p>
				<div class="col-md-6">
					<div class="input-group">
						<span class="input-group-addon" >Nom</span>
						<input class="form-control" ng-model="citerne.nom" required> </input>
					</div><br>
					<div class="input-group">
						<span class="input-group-addon">Station</span>
						<select ng-model="citerne.idstation" class="form-control" required>
						<option value="" >Choisir la station</option>
							<option ng-repeat="station in stations " value="{{::station.id}}">{{::station.nom}}
							</option> 
						</select>
					</div><br>
					 <div class="input-group">
					 <span class="input-group-addon">Carburant</span>
						<select class="form-control" ng-model="citerne.typecarburant" class="  form-control form-field " required>
							<option value="" >Type de carburant</option>
							<option value="Super98">Super 98</option> 
							<option value="Super95">Super 95</option> 
							<option value="Diesel">Diesel</option> 
							<option value="Mazout">Mazout</option> 							
						</select>
					</div><br>
				</div>	
				
				<div class="col-md-6">
					<div class="input-group">
					<span class="input-group-addon">Volume Max</span>
					<input class="form-control" pattern="\d*" ng-model="citerne.volumemax" placeholder="exemple : 1500 sans espace ni tirets" required> </input>
					<span class="input-group-addon">Litres</span>
					</div><br>
					
					<div class="input-group">
						<span class="input-group-addon">Volume Restant</span>
					<input class="form-control"  ng-model="citerne.volumerestant"  disabled> </input>
					<span class="input-group-addon">Litres</span>
					</div><br>
				</div>	
				
			</p>
			</div>
		</div>
		
		<div class="panel-footer text-right">
			<button class="btn btn-default" ng-click="nouveau()"  ng-show ="montreBtn4"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span>Nouveau </button>
			<button class="btn btn-danger" ng-click="supprimer(citerne.id)" ng-show ="montreBtn3"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span>Supprimer </button>
			<button class="btn btn-success" ng-click="myForm.$valid && enregistrer()" ng-show ="montreBtn1"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span>Créer</button>
			<button class="btn btn-success" ng-click="myForm.$valid && appliquer()" ng-show ="montreBtn2"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span>Valider</button></form>
		</div>
	<!-- FIN DE PANEL DEFAUT -->
	</div>
<!--  FIND DE COLONNE TABLE -->
</div>

Publicités