Nella prima parte del tutorial PHP CRUD abbiamo visto le impostazioni di base di collegamento al database e come inserire un nuovo record, ora andremo a leggere i dati e visualizzarli.
PHP CRUD – Lettura e impaginazione dei records in modalità OOP
Scriviamo un nuovo file nella cartella principale del nostro progetto e diamogli il nome di index.php. Chiaramente questa sarà la home page del progetto, copiamo ed incolliamo il seguente codice di partenza:
<?php
// set page header
$page_title = "Read Products";
include_once "header.php";
// contents will be here
// set page footer
include_once "footer.php";
?>
ora al posto di questa riga
// contents will be here
inseriamo il codice per mostrare un pulsante che, una volta cliccato, ci porterà alla pagina in cui possiamo creare un record,
echo '<div class="d-grid gap-2 d-md-flex justify-content-md-end"><a href="create_product.php" class="btn btn-primary">Create product</a></div>';
Andiamo a gestire l’impaginazione, una cosa che acquista importanza con il crescere dei records del database. Se ne hai molti evidentemente vuoi vederli ben impostati e leggibili, quindi inseriamo prima della riga:
// set page header
il seguente codice:
// page given in URL parameter, default page is one
$page = isset($_GET['page']) ? $_GET['page'] : 1;
// set number of records per page
$records_per_page = 5;
// calculate for the query LIMIT clause
$from_record_num = ($records_per_page * $page) - $records_per_page;
// retrieve records here
Lettura dei records dal database
definiti cosi i limiti di records per pagina per poter dare una impaginazione corretta, andiamo a leggere i dati dal database per cui inseriamo il codice seguente alla riga:
// retrieve records here
// include database and object files
include_once 'config/database.php';
include_once 'inc/product.php';
include_once 'inc/category.php';
// instantiate database and objects
$database = new Database();
$db = $database->getConnection();
$product = new Product($db);
$category = new Category($db);
// query products
$stmt = $product->readAll($from_record_num, $records_per_page);
$num = $stmt->rowCount();
in questo snippet di codice includiamo i files di configurazione del database ed i file contenenti le classi che ci servono per il progetto: Product e Category.
Poi instanziamo un nuovo oggetto Database() ed utilizziamo il metodo getConnection() della classe e di seguito instanziamo un nuovo oggetto Product ed un nuovo oggetto Category, dopo lanciamo la query di ricerca nel database attraverso il metodo readAll() della classe Product.
Aggiunta del metodo readAll() nella classe Product
Fatalmente se lanciassimo il file index.php adesso ci verrebbe restituito un errore, appunto perchè in effetti manca il metodo readAll() nella nostra classe Product, quindi andiamo ad inserirlo. Apriamo il file /inc/product.php ed inseriamo il seguente codice:
function readAll($from_record_num, $records_per_page){
$query = "SELECT
id, name, description, price, category_id
FROM
" . $this->table_name . "
ORDER BY
name ASC
LIMIT
{$from_record_num}, {$records_per_page}";
$stmt = $this->conn->prepare( $query );
$stmt->execute();
return $stmt;
}
dopo il metodo create(), facendo attenzione alle parentesi graffe per non incasinare il codice.
Visualizzazione dati
Adesso non avremo più errori, ma ancora non vengono visualizzati i records del database perchè ovviamente dobbiamo scrivere le istruzioni per visualizzarli correttamente, quindi andiamo ad inserire subito dopo il pulsante “Create product” questo codice che crea una tabella di visualizzazione dati.
// display the products if there are any
if($num>0){
echo "<table class='table table-hover table-responsive caption-top'>";
echo "<caption>List of products</caption>";
echo "<thead>";
echo "<tr>";
echo "<th scope='col'>Product</th>";
echo "<th scope='col'>Price</th>";
echo "<th scope='col'>Description</th>";
echo "<th scope='col'>Category</th>";
echo "<th scope='col'>Actions</th>";
echo "</tr>";
echo "</thead>";
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
extract($row);
echo '<tr scope="row">';
echo "<td>{$name}</td>";
echo "<td>{$price}</td>";
echo "<td>{$description}</td>";
echo "<td>";
$category->id = $category_id;
$category->readName();
echo $category->name;
echo "</td>";
echo "<td>";
// read one, edit and delete button will be here
echo "</td>";
echo "</tr>";
}
echo "</table>";
// paging buttons will be here
}
// tell the user there are no products
else{
echo "<div class='alert alert-info'>No products found.</div>";
}
Inserimento dei pulsanti di azione Leggi, Modifica ed Elimina
Lanciando il file index.php, che giunti a questo punto dovrebbe visualizzare in modo corretto la lista dei prodotti presenti nel nostro database ci accorgiamo che nella colonna “Actions” non c’è niente, perchè in questa colonna abbiamo previsto di inserire i pulsanti di azione che consentiranno il lancio delle operazioni di lettura, modifica oppure eliminazione di ogni record.
E allora dove vediamo il commento:
// read one, edit and delete button will be here
inseriamo questo codice per i pulsanti leggi, modifica ed elimina record.
// read, edit and delete buttons
echo "<a href='read_one.php?id={$id}' class='btn btn-primary left-margin'>
<span class='glyphicon glyphicon-list'></span> Read
</a>
<a href='update_product.php?id={$id}' class='btn btn-info left-margin'>
<span class='glyphicon glyphicon-edit'></span> Edit
</a>
<a delete-id='{$id}' class='btn btn-danger delete-object'>
<span class='glyphicon glyphicon-remove'></span> Delete
</a>";
Impostazione della impaginazione
Rimane da fare la paginazione dei records del database per la quale già precedentemente abbiamo disposto dei parametri nel file, e per definirla abbiamo bisogno di un altro file che chiameremo paging.php e creeremo nella cartella principale del progetto con questo codice:
<?php
echo "<nav aria-label='Page navigation example'>";
echo "<ul class='pagination'>";
// button for first page
if($page>1){
echo "<li class='page-item'><a class='page-link' href='{$page_url}' title='Go to the first page.'>";
echo "First";
echo "</a></li>";
}
// calculate total pages
$total_pages = ceil($total_rows / $records_per_page);
// range of links to show
$range = 2;
// display links to 'range of pages' around 'current page'
$initial_num = $page - $range;
$condition_limit_num = ($page + $range) + 1;
for ($x=$initial_num; $x<$condition_limit_num; $x++) {
// be sure '$x is greater than 0' AND 'less than or equal to the $total_pages'
if (($x > 0) && ($x <= $total_pages)) {
// current page
if ($x == $page) {
echo "<li class='page-item active'><a class='page-link' href=\"#\">$x <span class=\"sr-only\">(current)</span></a></li>";
}
// not current page
else {
echo "<li class='page-item'><a class='page-link' href='{$page_url}page=$x'>$x</a></li>";
}
}
}
// button for last page
if($page<$total_pages){
echo "<li class='page-item'><a class='page-link' href='" .$page_url. "page={$total_pages}' title='Last page is {$total_pages}.'>";
echo "Last";
echo "</a></li>";
}
echo "</ul>";
echo "</nav>";
?>
Abbiamo anche bisogno a questo scopo di un nuovo metodo countAll()
funzionale che si occuperà di eseguire quando serve il conteggio dei records presenti nel database, quindi inseriamo questo codice del metodo all’interno della classe Product nel file /inc/product.php, sempre facendo attenzione alle parentesi graffe per non creare errori.
// used for paging products
public function countAll(){
$query = "SELECT id FROM " . $this->table_name . "";
$stmt = $this->conn->prepare( $query );
$stmt->execute();
$num = $stmt->rowCount();
return $num;
}
per ultimo inseriamo il codice di inclusione del file paging.php, che andrà anche ad usare il metodo appena creato, all’interno del file index.php in modo da rendere effettiva l’impaginazione impostata.
// the page where this paging is used
$page_url = "index.php?";
// count all products in the database to calculate total pages
$total_rows = $product->countAll();
// paging buttons here
include_once 'paging.php';
Se tutto è andato per il verso giusto dovremmo vedere la home page del nostro progetto in modo corretto con l’impaginazione funzionante che ci consentirà di girare le pagine e visualizzare tutti i prodotti presenti nel database.
Aggiornamento di record in PHP in modalità OOP
Il nuovo passo riguarda la modifica dei records dei prodotti sempre usando la metodologia OOP. Adesso abbiamo bisogno di creare un nuovo file chiamato update_product.php nella cartella principale del progetto ed iniziamo a farlo copiando ed incollando questo codice di base:
<?php
// retrieve one product will be here
// set page header
$page_title = "Update Product";
include_once "header.php";
// contents will be here
// set page footer
include_once "footer.php";
?>
poi al posto della riga
// contents will be here
inseriamo il solito pulsante
echo '<div class="d-grid gap-2 d-md-flex justify-content-md-end"><a href="index.php" class="btn btn-primary">Read Products</a></div>';
che ci riporta alla pagina principale del progetto. Di seguito abbiamo però bisogno di “trovare” esattamente il record da modificare e lo facciamo tramite l’ID, per cui inseriamo dopo il pulsante questo codice:
// get ID of the product to be edited
$id = isset($_GET['id']) ? $_GET['id'] : die('ERROR: missing ID.');
// include database and object files
include_once 'config/database.php';
include_once 'inc/product.php';
include_once 'inc/category.php';
// get database connection
$database = new Database();
$db = $database->getConnection();
// prepare objects
$product = new Product($db);
$category = new Category($db);
// set ID property of product to be edited
$product->id = $id;
// read the details of product to be edited
$product->readOne();
che in pratica preleva tramite GET l’ID relativo alla riga di records che dobbiamo andare a modificare e nel caso non esiste interrompe le operazioni ed esce dall’esecuzione ritornando un messaggio di “ID non trovato“, recupera ed include i soliti necessari files di configurazione e delle classi Product e Category, poi istanzia di nuovo gli oggetti necessari dalle rispettive classi e identifica l’ID tramite la proprietà che avevamo già impostato nella classe Product poi va a leggere il record relativo attraverso il metodo readOne() della stessa classe……ma un momento ….. questo metodo non c’è ancora nella classe Product, quindi, ovviamente, dobbiamo crearlo e lo insieriamo nella classe Product nel file /inc/product.php attraverso questo codice:
function readOne(){
$query = "SELECT
name, price, description, category_id
FROM
" . $this->table_name . "
WHERE
id = ?
LIMIT
0,1";
$stmt = $this->conn->prepare( $query );
$stmt->bindParam(1, $this->id);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$this->name = $row['name'];
$this->price = $row['price'];
$this->description = $row['description'];
$this->category_id = $row['category_id'];
}
Giunti a questo punto c’è bisogno di inserire un form nel file update-product.php dove inserire il record trovato con l’ID per la modifica e poi salvare la modifica fatta.
Inseriamo il seguente codice nel file indicato:
<!-- post code will be here -->
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"] . "?id={$id}");?>" method="post">
<table class='table table-hover table-responsive table-bordered'>
<tr>
<td>Name</td>
<td><input type='text' name='name' value='<?php echo $product->name; ?>' class='form-control' /></td>
</tr>
<tr>
<td>Price</td>
<td><input type='text' name='price' value='<?php echo $product->price; ?>' class='form-control' /></td>
</tr>
<tr>
<td>Description</td>
<td><textarea name='description' class='form-control'><?php echo $product->description; ?></textarea></td>
</tr>
<tr>
<td>Category</td>
<td>
<!-- categories select drop-down will be here -->
</td>
</tr>
<tr>
<td></td>
<td>
<button type="submit" class="btn btn-primary">Update</button>
</td>
</tr>
</table>
</form>
Se tutto è ok cliccando sul pulsante “Edit” della home page dovrebbe apparire nella pagina di modifica prodotto il form compilato con i dati del record trovato.
Però manca la categoria quindi abbiamo ancora bisogno di inserire del codice nel form al posto della riga
<!-- categories select drop-down will be here -->
per iterare la tabella categorie del database e associare la categoria corretta in modifica.
<?php
$stmt = $category->read();
// put them in a select drop-down
echo "<select class='form-control' name='category_id'>";
echo "<option>Please select...</option>";
while ($row_category = $stmt->fetch(PDO::FETCH_ASSOC)){
$category_id=$row_category['id'];
$category_name = $row_category['name'];
// current category of the product must be selected
if($product->category_id==$category_id){
echo "<option value='$category_id' selected>";
}else{
echo "<option value='$category_id'>";
}
echo "$category_name</option>";
}
echo "</select>";
?>
Utilizziamo il metodo read() della classe Category per confrontare e trovare la categoria attraverso il suo ID e assegnarla correttamente al prodotto per poterlo poi eventualmente modificare.
Ora abbiamo tutti i dati per modificare il prodotto, possiamo fare le modifiche desiderate e poi eventualmente salvare nel database