Создание базового RESTful (CRUD) с помощью Golang и MySQL

Crud with login & register in php & mysql (add, edit, delete, view) – mukesh chapagain blog

In another article, I have written about Very Simple Add, Edit, Delete, View in PHP & MySQL. That article contains a basic CRUD (Create, Read, Update, Delete) system. It doesn’t have the feature of login and register.

In this article, I will be presenting a complete CRUD system containing login and register feature. User should register himself first. And then he can add data after logging in.

Here is the step-by-step guide on creating such CRUD system:

First of all, we need to create database and tables. Let the database name be ‘test2‘.

create database `test2`;

There are two tables in the system: login and products.

`login`: This table contains user’s information for login. It contains user’s name, email, username and password. User registration data is saved into this table.

`products`: This table contains data added by logged in users. This table contains product information like name, quantity, and price.


use `test2`;

CREATE TABLE `login` (
    `id` int(9) NOT NULL auto_increment,
    `name` varchar(100) NOT NULL,
    `email` varchar(100) NOT NULL,
    `username` varchar(100) NOT NULL,
    `password` varchar(100) NOT NULL,  
    PRIMARY KEY  (`id`)
) ENGINE=InnoDB;

CREATE TABLE `products` (
    `id` int(11) NOT NULL auto_increment,
    `name` varchar(100) NOT NULL,
    `qty` int(5) NOT NULL,
    `price` decimal(10,2) NOT NULL,
    `login_id` int(11) NOT NULL,
    PRIMARY KEY  (`id`),
    CONSTRAINT FK_products_1
    FOREIGN KEY (login_id) REFERENCES login(id)
    ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE=InnoDB;

Note: Cascading is done in `products` table making `login_id` as foreign key to `login` table. This means that all the products entered by a user is automatically deleted from `products` table whenever that particular user is deleted from `login` table.

Now, we will create a `connection.php` file which contains database connection code. This code connects to the MySQL database. This file is included in all PHP pages where database connection is necessary.

connection.php

In below code, the database host name is `localhost` where `username=root` and `password=root`. The database `test2` has been selected.


<?php
/*
// mysql_connect("database-host", "username", "password")
$conn = mysql_connect("localhost","root","root") 
			or die("cannot connected");

// mysql_select_db("database-name", "connection-link-identifier")
@mysql_select_db("test2",$conn);
*/

/**
 * mysql_connect is deprecated
 * using mysqli_connect instead
 */

$databaseHost = 'localhost';
$databaseName = 'test2';
$databaseUsername = 'root';
$databasePassword = 'root';

$mysqli = mysqli_connect($databaseHost, $databaseUsername, $databasePassword, $databaseName); 
?>

`index.php` is our homepage. In this page, if the user is not logged in then login and register links are displayed. If the user is logged in then the user is greeted and a link is displayed to view & add products.

Note: `session_start()` function should be written at the beginning of every php file where session handling is done. You can see the same in `index.php` as well.

index.php


<?php session_start(); ?>
<html>
<head>
    <title>Homepage</title>
    <link href="style.css" rel="stylesheet" type="text/css">
</head>

<body>
    <div id="header">
        Welcome to my page!
    </div>
    <?php
    if(isset($_SESSION['valid'])) {			
        include("connection.php");					
        $result = mysqli_query($mysqli, "SELECT * FROM login");
    ?>				
        Welcome <?php echo $_SESSION['name'] ?> ! <a href='logout.php'>Logout</a><br/>
        <br/>
        <a href='view.php'>View and Add Products</a>
        <br/><br/>
    <?php	
    } else {
        echo "You must be logged in to view this page.<br/><br/>";
        echo "<a href='login.php'>Login</a> | <a href='register.php'>Register</a>";
    }
    ?>
    <div id="footer">
        Created by <a href="http://blog.chapagain.com.np" title="Mukesh Chapagain">Mukesh Chapagain</a>
    </div>
</body>
</html>

`index.php` uses a little bit of Cascading StyleSheet (CSS). Here is the CSS file code used in index.php.

style.css


body {
	margin: auto;
	height: 500px;
	padding: 20px;
}

#header {
	width: 700px;
	color: maroon;
	font-size: 32px;
	padding: 10px 10px 10px 0px;
	margin-bottom: 15px;
	border-bottom: 1px solid green;
}

#footer {
	border-top: 1px solid green;
	margin-top: 20px;
	color: #336699;
	padding-top: 10px;	
}

Registration page asks for user’s name, email, username and password. The registration data is saved in `login` table.

register.php


<html>
<head>
    <title>Register</title>
</head>

<body>
    <a href="index.php">Home</a> <br />
    <?php
    include("connection.php");

    if(isset($_POST['submit'])) {
        $name = $_POST['name'];
        $email = $_POST['email'];
        $user = $_POST['username'];
        $pass = $_POST['password'];

        if($user == "" || $pass == "" || $name == "" || $email == "") {
            echo "All fields should be filled. Either one or many fields are empty.";
            echo "<br/>";
            echo "<a href='register.php'>Go back</a>";
        } else {
            mysqli_query($mysqli, "INSERT INTO login(name, email, username, password) VALUES('$name', '$email', '$user', md5('$pass'))")
            or die("Could not execute the insert query.");
			
            echo "Registration successfully";
            echo "<br/>";
            echo "<a href='login.php'>Login</a>";
        }
    } else {
?>
        <p><font size=" 2">Register</font></p>
        <form name="form1" method="post" action="">
            <table width="75%" border="0">
                <tr> 
                    <td width="10%">Full Name</td>
                    <td><input type="text" name="name"></td>
                </tr>
                <tr> 
                    <td>Email</td>
                    <td><input type="text" name="email"></td>
                </tr>			
                <tr> 
                    <td>Username</td>
                    <td><input type="text" name="username"></td>
                </tr>
                <tr> 
                    <td>Password</td>
                    <td><input type="password" name="password"></td>
                </tr>
                <tr> 
                    <td> </td>
                    <td><input type="submit" name="submit" value="Submit"></td>
                </tr>
            </table>
        </form>
    <?php
    }
    ?>
</body>
</html>

After successful registration, user needs to login in order to add products in the system.

When the login is successful, I have set three SESSION variables. You can see it in below `login.php` code.

SESSION[‘valid’] = This variable contains user’s username
SESSION[‘name’] = This variable contains user’s full name
SESSION[‘id’] = This variable contains user’s id

login.php


<?php session_start(); ?>
<html>
<head>
    <title>Login</title>
</head>

<body>
<a href="index.php">Home</a> <br />
<?php
include("connection.php");

if(isset($_POST['submit'])) {
    $user = mysqli_real_escape_string($mysqli, $_POST['username']);
    $pass = mysqli_real_escape_string($mysqli, $_POST['password']);

    if($user == "" || $pass == "") {
        echo "Either username or password field is empty.";
        echo "<br/>";
        echo "<a href='login.php'>Go back</a>";
    } else {
        $result = mysqli_query($mysqli, "SELECT * FROM login WHERE username='$user' AND password=md5('$pass')")
        or die("Could not execute the select query.");
		
        $row = mysqli_fetch_assoc($result);
		
        if(is_array($row) && !empty($row)) {
            $validuser = $row['username'];
            $_SESSION['valid'] = $validuser;
            $_SESSION['name'] = $row['name'];
            $_SESSION['id'] = $row['id'];
        } else {
            echo "Invalid username or password.";
            echo "<br/>";
            echo "<a href='login.php'>Go back</a>";
        }

        if(isset($_SESSION['valid'])) {
            header('Location: index.php');			
        }
    }
} else {
?>
    <p><font size=" 2">Login</font></p>
    <form name="form1" method="post" action="">
        <table width="75%" border="0">
            <tr> 
                <td width="10%">Username</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr> 
                <td>Password</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr> 
                <td> </td>
                <td><input type="submit" name="submit" value="Submit"></td>
            </tr>
        </table>
    </form>
<?php
}
?>
</body>
</html>

After successful login, user is redirected to homepage (index.php). There is a link to view and add new products.

To add product data into database, we need an html form.

add.html


<html>
<head>
    <title>Add Data</title>
</head>

<body>
    <a href="index.php">Home</a> | <a href="view.php">View Products</a> | <a href="logout.php">Logout</a>
    <br/><br/>
    
    <form action="add.php" method="post" name="form1">
        <table width="25%" border="0">
            <tr> 
                <td>Name</td>
                <td><input type="text" name="name"></td>
            </tr>
            <tr> 
                <td>Quantity</td>
                <td><input type="text" name="qty"></td>
            </tr>
            <tr> 
                <td>Price</td>
                <td><input type="text" name="price"></td>
            </tr>
            <tr> 
                <td></td>
                <td><input type="submit" name="Submit" value="Add"></td>
            </tr>
        </table>
    </form>
</body>
</html>

Form action on `add.html` is `add.php`. It means that the submitted form data will go to `add.php`. In `add.php`, we do a simple validation of checking if the entered name, quantity & price are empty or not. If they are all filled then the data will be inserted into database table.

add.php


<?php session_start(); ?>

<?php
if(!isset($_SESSION['valid'])) {
    header('Location: login.php');
}
?>

<html>
<head>
    <title>Add Data</title>
</head>

<body>
<?php
//including the database connection file
include_once("connection.php");

if(isset($_POST['Submit'])) {	
    $name = $_POST['name'];
    $qty = $_POST['qty'];
    $price = $_POST['price'];
    $loginId = $_SESSION['id'];
		
    // checking empty fields
    if(empty($name) || empty($qty) || empty($price)) {				
        if(empty($name)) {
            echo "<font color='red'>Name field is empty.</font><br/>";
        }
		
        if(empty($qty)) {
            echo "<font color='red'>Quantity field is empty.</font><br/>";
        }
		
        if(empty($price)) {
            echo "<font color='red'>Price field is empty.</font><br/>";
        }
		
        //link to the previous page
        echo "<br/><a href='javascript:self.history.back();'>Go Back</a>";
    } else { 
        // if all the fields are filled (not empty) 
			
        //insert data to database	
        $result = mysqli_query($mysqli, "INSERT INTO products(name, qty, price, login_id) VALUES('$name','$qty','$price', '$loginId')");
		
        //display success message
        echo "<font color='green'>Data added successfully.";
        echo "<br/><a href='view.php'>View Result</a>";
    }
}
?>
</body>
</html>

Products data is fetched from database and displayed in `view.php` file. This file also contains a link to add data. On every row of displayed data, there is also a link to edit and delete data. Below is a sample image of `view.php`:

CRUD PHP MySQL

At the beginning of `view.php` file below and `add.php` file above, you must have noticed `session_start()` and `isset()` function.

`session_start()` function should be written at the beginning of every page where SESSION handling is done.

`isset()` function checks if the SESSION variable is set or not. If not, the user will be redirected to login page (login.php).

view.php


<?php session_start(); ?>

<?php
if(!isset($_SESSION['valid'])) {
    header('Location: login.php');
}
?>

<?php
//including the database connection file
include_once("connection.php");

//fetching data in descending order (lastest entry first)
$result = mysqli_query($mysqli, "SELECT * FROM products WHERE login_id=".$_SESSION['id']." ORDER BY id DESC");
?>

<html>
<head>
    <title>Homepage</title>
</head>

<body>
<a href="index.php">Home</a> | <a href="add.html">Add New Data</a> | <a href="logout.php">Logout</a>
<br/><br/>
	
<table width='80%' border=0>
    <tr bgcolor='#CCCCCC'>
        <td>Name</td>
        <td>Quantity</td>
        <td>Price (euro)</td>
        <td>Update</td>
    </tr>
    <?php
    while($res = mysqli_fetch_array($result)) {		
        echo "<tr>";
        echo "<td>".$res['name']."</td>";
        echo "<td>".$res['qty']."</td>";
        echo "<td>".$res['price']."</td>";	
        echo "<td><a href="edit.php?id=$res[id]">Edit</a> | <a href="delete.php?id=$res[id]" onClick="return confirm('Are you sure you want to delete?')">Delete</a></td>";		
    }
    ?>
</table>	
</body>
</html>

Each row of data can be edited separately. Row ID is passed in the URL of `edit.php`. ID uniquely identifies the data entry.

While adding data, we had two files: `add.html` and `add.php`. While editing data, I have kept the entire thing in a single `edit.php` file. Edit form in HTML and database update code in PHP are present in the same file.

In the code below, at first a single row entry of data is fetched based on the `id`. The fetched data is displayed in the edit form. When user edits the data and submits the form, then some simple validation is done for empty data. When everything is correct, then that particular entry of data is updated in database.

edit.php


<?php session_start(); ?>

<?php
if(!isset($_SESSION['valid'])) {
    header('Location: login.php');
}
?>

<?php
// including the database connection file
include_once("connection.php");

if(isset($_POST['update']))
{	
    $id = $_POST['id'];
	
    $name = $_POST['name'];
    $qty = $_POST['qty'];
    $price = $_POST['price'];	
	
    // checking empty fields
    if(empty($name) || empty($qty) || empty($price)) {				
        if(empty($name)) {
            echo "<font color='red'>Name field is empty.</font><br/>";
        }
		
        if(empty($qty)) {
            echo "<font color='red'>Quantity field is empty.</font><br/>";
        }
		
        if(empty($price)) {
            echo "<font color='red'>Price field is empty.</font><br/>";
        }		
    } else {	
        //updating the table
        $result = mysqli_query($mysqli, "UPDATE products SET name='$name', qty='$qty', price='$price' WHERE id=$id");
		
        //redirectig to the display page. In our case, it is view.php
        header("Location: view.php");
    }
}
?>
<?php
//getting id from url
$id = $_GET['id'];

//selecting data associated with this particular id
$result = mysqli_query($mysqli, "SELECT * FROM products WHERE id=$id");

while($res = mysqli_fetch_array($result))
{
    $name = $res['name'];
    $qty = $res['qty'];
    $price = $res['price'];
}
?>
<html>
<head>	
    <title>Edit Data</title>
</head>

<body>
    <a href="index.php">Home</a> | <a href="view.php">View Products</a> | <a href="logout.php">Logout</a>
    <br/><br/>
	
    <form name="form1" method="post" action="edit.php">
        <table border="0">
            <tr> 
                <td>Name</td>
                <td><input type="text" name="name" value="<?php echo $name;?>"></td>
            </tr>
            <tr> 
                <td>Quantity</td>
                <td><input type="text" name="qty" value="<?php echo $qty;?>"></td>
            </tr>
            <tr> 
                <td>Price</td>
                <td><input type="text" name="price" value="<?php echo $price;?>"></td>
            </tr>
            <tr>
                <td><input type="hidden" name="id" value=<?php echo $_GET['id'];?>></td>
                <td><input type="submit" name="update" value="Update"></td>
            </tr>
        </table>
    </form>
</body>
</html>

Each row of data can be deleted separately. Row ID is passed in the URL of `delete.php`. ID uniquely identifies the data entry. After deletion, the user is redirected to view page (view.php).

delete.php


<?php session_start(); ?>

<?php
if(!isset($_SESSION['valid'])) {
    header('Location: login.php');
}
?>

<?php
//including the database connection file
include("connection.php");

//getting id of the data from url
$id = $_GET['id'];

//deleting the row from table
$result=mysqli_query($mysqli, "DELETE FROM products WHERE id=$id");

//redirecting to the display page (view.php in our case)
header("Location:view.php");
?>

User can finally logout. User is redirected to homepage (index.php) after logout.

logout.php


session_start();
session_destroy();
header("Location:index.php");

Download Full Source Code: Complete Create, Read, Update, Delete in PHP & MySQL

Hope this helps. Thanks.

Исключения

Исключения в яве делятся на два типа — checked и unchecked.

Первые являются ожидаемыми исключениями при моделировании бизнес-процесса. Например, если вы сохраняете буклист с
несуществующей книгой. Или, например, вы пытаетесь купить билет в кинотеатр, а мест уже нет. То есть ошибки абсолютно
нормальные, про возможность их возникновения вы знаете и знаете условия их возникновения. Сам язык заставляет делать
варианты обработок этих ошибок.

Вторые бросаются если происходит нештатная ситуация. База упала, кончилась память, вы не сделали проверку на null и т.д.
Unchecked потому что не надо их проверять, так как они появляются неожиданно, как татаро-монгольское иго.

Задумка хорошая, но после 100500 try-catch где надо и не надо, все пришли к выводу, что давайте все исключения будут
unchecked. А программист сам решает, проверять или нет.

По поводу обработок исключений.

??? danger “Warning”

Для сервисного слоя, если у вас есть специфичные ошибки, то я предлагаю завести специальный класс ServiceException.

И его “неожиданный наследник”:

И пример исключения:

messages.properties:

Обычной практикой является создание единого ControllerAdvice, который занимается
только обработкой ошибок.

@ControllerAdvicepublicclassExceptionHandlerAdvice// Стандартный интерфейс спринга, который нужен для// true инжекта messageSource (штука, для доступа к message.properties implementsMessageSourceAware {

    privateMessageSourcemessageSource;

    // Обрабатывыаем наши собственные ошибки@ExceptionHandler(ServiceException.class)
    publicResponseEntity<Map<String, String>> handle(
            ServiceExceptionex, Localelocale) {
        HttpStatusstatusCode = exinstanceofUnexpectedServiceException 
                                        ? INTERNAL_SERVER_ERROR : BAD_REQUEST;
        Stringmessage = messageSource.getMessage(
                ex.getMessageCode(), ex.getArgs(), locale);
        returnstatus(statusCode)
                    .body(Collections.singletonMap("error", message));
    }

    // Обрабатываем ошибки нарушения ограничений реляционной СУБД.// Например, при попытки связать сущность с несуществующей сущностью@ExceptionHandler(DataIntegrityViolationException.class)
    publicResponseEntity<Map<String, String>> handle(
            DataIntegrityViolationExceptionex, Localelocale) {
        Stringmessage = messageSource.getMessage("conflict", null, locale);
        returnstatus(CONFLICT).body(Collections.singletonMap("error", message));
    }

    @OverridepublicvoidsetMessageSource(MessageSourcemessageSource) {
        this.messageSource = messageSource;
    }

    // Ошибки валидации@ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBodypublicMap<String, String> handle(
            ConstraintViolationExceptionex, Localelocale) {
        returnex.getConstraintViolations()
                .stream()
                .collect(toMap(
                        v -> v.getPropertyPath().toString(), 
                        ConstraintViolation::getMessage
                        )
                    );

    }

    // Тоже ошибки валидации@ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBodypublicMap<String, String> handle(
            MethodArgumentNotValidExceptione, Localelocale) {
        returne.getBindingResult()
                .getFieldErrors()
                .stream()
                .collect(toMap(
                        FieldError::getField, 
                        error -> messageSource.getMessage(error, locale)
                        )
                    );
    }

}

Пошаговая разработка crud веб-приложения на codeigniter 4, mysql и bootstrap

В этой статье мы рассмотрим поэтапную разработку CRUD операций для приложения на основе CodeIgniter 4 и MySQL. Мы подробно разберем создание функциональности CRUD для взаимодействия с базой данных MySQL, добавим две дополнительные операции для поиска записей и разбиения на страницы, а также сделаем интерфейс с помощью Bootstrap.

CRUD – общепринятое сокращенное название набора из 4 базовых операций для работы с базами данных. Операции по созданию, чтению, модификации и удалению записей помогают сделать сайт динамическим. Ниже мы рассмотрим разработку CRUD на CodeIgniter 4. Готовое приложение будет создавать, редактировать, читать и удалять записи каталога товаров в базе данных MySQL.

Bootstrap – самый популярный и мощный фреймворк для быстрой разработки фронтенда. С его помощью мы быстро создадим стильный и адаптивный интерфейс для нашего приложения. Подробные уроки по Bootstrap есть в соответствующем разделе сайта.

DataTables – плагин для библиотеки jQuery. Это мощный и гибкий инструмент для добавления продвинутой функциональности к любой HTML таблице.

Для ускорения разработки CRUD нам прежде всего нужно скачать и установить PHP фреймворк CodeIgniter 4. Распакуйте архив в корневую папку проекта. Если вы используете локальный сервер XAMPP, файлы нужно поместить в папку xampphtdocs. При использовании ОС Ubuntu файлы помещают в директорию /var/www или /var/www/html.

Чтобы получать уведомления об ошибках, нужно изменить значение параметра display_errors в файлах app/Config/Boot/development.php и app/Config/Boot/production.php с 0 на 1:

Удалите указание на index.php с помощью изменения значения параметра $indexPage = ‘ ‘ в файле ConfigApp.php, как показано ниже.

Откройте файл appConfigDatabase.php и введите все необходимые данные – название базы, имя пользователя, пароль и так далее.

/**
 * The default database connection.
 *
 * @var array
 */
public $default = [
	'DSN'      => '',
	'hostname' => 'localhost',
	'username' => 'root',
	'password' => '',
	'database' => 'codeigniter_crud',
	'DBDriver' => 'MySQLi',
	'DBPrefix' => '',
	'pConnect' => false,
	'DBDebug'  => (ENVIRONMENT !== 'production'),
	'charset'  => 'utf8',
	'DBCollat' => 'utf8_general_ci',
	'swapPre'  => '',
	'encrypt'  => false,
	'compress' => false,
	'strictOn' => false,
	'failover' => [],
	'port'     => 3306,
];

Выполните приведенную ниже SQL команду для создания таблицы под названием products («товары») в вашей базе данных MySQL. С этой таблицей будет работать наше CRUD приложение.

Создайте файл модели appModelsProduct.php в папке appModels и сохраните в нем приведенный ниже код. Модель – это PHP класс, который предназначен для работы с информацией в вашей базе данных.

На этом этапе мы создадим контроллер под названием ProductController.php. Этот контроллер будет управлять всеми CRUD операциями – созданием, модификацией, чтением и удалением записей. Сохраните в файле контроллера appControllersProductController.php приведенный ниже код.

<?php
namespace AppControllers;
use AppModelsProduct;
use CodeIgniterController;

class ProductController extends Controller
{
    // вывод списка товаров
    public function index() {
        $product = new Product();
        $data['products'] = $product->orderBy('id', 'DESC')->findAll();
        return view('products/index', $data);
    }

    // форма добавления товара
    public function create() {
        return view('products/create');
    }

    // сохранение информации о товаре
    public function store() {
        $product = new Product();

        $data = [
            'name' => $this->request->getVar('name'),
            'price'  => $this->request->getVar('price'),
            'description'  => $this->request->getVar('description'),
        ];

        $product->insert($data);

        return $this->response->redirect(site_url('/products'));
    }

    // вывод информации о товаре
    public function edit($id) {
        $product = new Product();
        $data['product'] = $product->where('id', $id)->first();
        return view('products/edit', $data);
    }

    // изменение информации о товаре
    public function update() {
        $product = new Product();
        $id = $this->request->getVar('id');

        $data = [
            'name' => $this->request->getVar('name'),
            'price'  => $this->request->getVar('price'),
            'description'  => $this->request->getVar('description'),
        ];

        $product->update($id, $data);
        return $this->response->redirect(site_url('/products'));
    }

    // удаление позиции
    public function delete($id) {
        $product = new Product();
        $data['product'] = $product->where('id', $id)->delete($id);
        return $this->response->redirect(site_url('/products'));
    }
}

Чтобы обеспечить выполнение всех CRUD операций, необходимо создать соответствующие маршруты. Для этого откройте файл appConfigRoutes.php и сохраните в нем данный код.

Перейдем к созданию страниц, которые обеспечивают отображение каталога товаров, а также форм создания и редактирования записей.

Прежде всего создайте новую папку products, в которой будет располагаться главная страница приложения index.php с каталогом товаров. Вверху списка товаров находится кнопка для добавления новых позиций. Сохраните в файле appViewsproductsindex.php приведенный ниже код.

После добавления в таблицу products тестовых данных главная страница приложения будет выглядеть примерно так, как показано ниже.

Для добавления в таблицу новых позиций мы создадим файл create.php внутри папки products. Сохраните в файле appViewsproductscreate.php приведенный ниже код, который обеспечивает создание веб-формы для добавления записей в базу данных.

<!DOCTYPE html>
<html>

<head>
  <title>Codeigniter 4 Crud with Validation Demo</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
  <style>
    .container {
      max-width: 500px;
    }

    .error {
      display: block;
      padding-top: 5px;
      font-size: 14px;
      color: red;
    }
  </style>
</head>

<body>
  <div class="container mt-5">
    <form method="post" id="createProduct" action="<?= site_url('/products/store') ?>">
      <div class="form-group">
        <label>Name</label>
        <input type="text" name="name" class="form-control">
      </div>

      <div class="form-group">
        <label>Price</label>
        <input type="number" name="price" class="form-control">
      </div>

      <div class="form-group">
        <label>Description</label>
        <textarea type="text" name="description" class="form-control"></textarea>
      </div>

      <div class="form-group">
        <button type="submit" class="btn btn-primary btn-block">Save</button>
      </div>
    </form>
  </div>

  <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.2/jquery.validate.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.2/additional-methods.min.js"></script>
  <script>
    if ($("#createProduct").length > 0) {
      $("#createProduct").validate({
        rules: {
          name: {
            required: true,
          },
          price: {
            required: true,
          },
          description: {
            required: true,
          },
        },
        messages: {
          name: {
            required: "Name is required.",
          },
          price: {
            required: "Price is required.",
          },
          description: {
            required: "Description is required.",
          },
        },
      })
    }
  </script>
</body>

</html>

Для редактирования данных товаров по ID создайте файл appViewsproductsedit.php и сохраните в нем приведенный ниже код.

<!DOCTYPE html>
<html>

<head>
  <title>Codeigniter 4 Crud with Validation Demo</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
  <style>
    .container {
      max-width: 500px;
    }

    .error {
      display: block;
      padding-top: 5px;
      font-size: 14px;
      color: red;
    }
  </style>
</head>

<body>
  <div class="container mt-5">
    <form method="post" id="createProduct" action="<?= site_url('/products/update') ?>">
    <input type="hidden" name="id" value="<?php echo $product['id']; ?>">
        <div class="form-group">
            <label>Name</label>
            <input type="text" name="name" class="form-control" value="<?php echo $product['name']; ?>">
        </div>
        <div class="form-group">
            <label>Price</label>
            <input type="number" name="price" class="form-control" value="<?php echo $product['price']; ?>">
        </div>
        <div class="form-group">
            <label>Description</label>
            <textarea type="text" name="description" class="form-control"><?php echo $product['description']; ?></textarea>
        </div>
        <div class="form-group">
            <button type="submit" class="btn btn-primary btn-block">Save</button>
        </div>
    </form>
  </div>

  <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.2/jquery.validate.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.2/additional-methods.min.js"></script>
  <script>
    if ($("#createProduct").length > 0) {
      $("#createProduct").validate({
        rules: {
          name: {
            required: true,
          },
          price: {
            required: true,
          },
          description: {
            required: true,
          },
        },
        messages: {
          name: {
            required: "Name is required.",
          },
          price: {
            required: "Price is required.",
          },
          description: {
            required: "Description is required.",
          },
        },
      })
    }
  </script>
</body>

</html>

Для запуска приложения сначала выполните команду:

Затем откройте главную страницу приложения в браузере по адресу:

Создание функциональности CRUD для приложения на CodeIgniter 4 и MySQL с Bootstrap интерфейсом завершено. Для лучшего понимания принципов работы CRUD на PHP рекомендую обратиться к этому уроку. Надеюсь, статья вам пригодилась, не забудьте поделиться с друзьями.

Пожалуйста, оставляйте ваши комментарии по текущей теме материала. Мы очень благодарим вас за ваши комментарии, отклики, дизлайки, подписки, лайки!

Создание моделей

Модели можно сгенерировать либо по таблицам в базе (способ поищите сами, их много), либо написать руками.

Код моделей можете посмотреть на гитхабе, на примере разберем только несколько.

Пользователь:

@Entity@Table(name = "users")
publicclassUserimplementsUserDetails { // А вот это интерфейс Spring Security, // маркер того, что класс хранит  информацию о пользователе@IdprivateStringlogin;

    // На самом деле здесь не хранятся пароли. Здесь хранится его хэш.// Это необходимо для того, чтобы не хранить пароли в базе в открытом виде.// Работает это довольно просто. Нам извне приходит пароль (при логине пользователя)// Внешний пароль мы хэшируем и проверяем, совпадает ли он с хэшом из базы. // Так как для одинаковых паролей хэш будет одинаков, то все окprivateStringpassword;

    privateStringdisplayName;

    @ManyToMany(cascade = ALL, mappedBy = "users", fetch = EAGER)
    privateSet<Booklist> booklists = newHashSet<>();

    // Здесь хранятся роли пользователя, админ там, секретарша или типа того// Но у нас будет только одна роль, поэтому в базе мы ее не храним и забиваем // гвоздями. Не забудь только про ROLE_, без него не будет работать. @OverridepublicCollection<? extendsGrantedAuthority> getAuthorities() {
        returnCollections.singleton(newSimpleGrantedAuthority("ROLE_USER"));
    }

    @OverridepublicStringgetPassword() {
        returnpassword;
    }

    @OverridepublicStringgetUsername() {
        returnlogin;
    }

    // Ниже идет всякая фигня, если мы как-то хотим забанить пользователя@OverridepublicbooleanisAccountNonExpired() {
        returntrue;
    }

    @OverridepublicbooleanisAccountNonLocked() {
        returntrue;
    }

    @OverridepublicbooleanisCredentialsNonExpired() {
        returntrue;
    }

    @OverridepublicbooleanisEnabled() {
        returntrue;
    }

    publicUseraddBooklist(Booklistbooklist) {
        booklists.add(booklist);
        returnthis;
    }

    @Overridepublicbooleanequals(Objecto) {
        if (this == o) returntrue;
        if (o == null || getClass() != o.getClass()) returnfalse;
        Useruser = (User) o;
        returnObjects.equals(login, user.login);
    }
    
    @OverridepublicinthashCode() {
        returnObjects.hash(login);
    }
    
    // Геттеры & Сеттеры
}    

Список книг:

Надо еще бы указать equals и hashCode, но сделаем это позже.

??? “Немного про lombok”
Многие любят использовать lombok при написания моделей. Я этого делать не советую по некоторым причинам:

* В сущностях и так слишком много аннотаций. Серьезно, только над самим классом уже две. А хотелось бы добавить
`@Data`, `@Accessor(chain = true)` (позволяет сеттить значения полей более удобно). Но в итоге весь класс
оказывается увешан аннотациями так, что даже кода под ним не видно.
* С аннотацией `@Data` тоже не все однозначно. Допустим у нам обычная bi-direction связь, например, книга
ссылается на издателя, а издатель на свои книги. Если мы повесим эту аннотацию на класс книги и издателя, то после
вызова equals & hashcode приложение уйдет в бесконечный цикл. Это происходит, потому что equals & hashcode 
генерируются для всех полей. То есть у издателя тоже будет вызван метод equals & hashcode, так как он является
полем книги, а в нем  есть поле с книгами, у которых этим методы тоже будут вызваны и так до бесконечности.
Чтобы этого избежать, можно повесить аннотацию `@EqualsAndHashCode(exclude = {"books"}`, а это опять   1
аннотация. 

Исходя из озвученных причин я советую избегать использования lombok'a в сущностях

Создание своего контроллера

В контроллере мы экспортируем созданные нами в модели функции. Если в общем, то каждый раз мы валидизируем запрос проверяя не было ли отправлено пустое тело (если вы планируете трансформить API в открытое, то это must have, да и вообще это полезно). Дальнейшие действия отличаются, в зависимости от того, нужно ли чтение

res.id

для выполнения запроса, или это не уточненный запрос. В методе

findAll

я оставил разрешающие все заголовки ответа для того, чтобы упростить тестирование своего API.

    
const Deal = require("../models/deal.model.js");

//Создаем и сохраняем новое дело
exports.create = (req, res) => {
  //  Валидизируем запрос
  if (!req.body) {
    res.status(400).send({
      message: "У нас не может не быть контента"
    });
  }

  // создание своего дела

  const deal = new Deal({
    text: req.body.text,
    inner_key: req.body.inner_key
    // у нашего дела будет текст и внутренний id, который будет использоваться как 
    // ключ для элементов в React
  });


  Deal.create(deal, (err, data) => {
    if (err)
      res.status(500).send({
        message:
          err.message || "Произошла ошибка во время выполнения кода"
      });
    else res.send(data);
  });
};

// Получение всех пользователей из базы данных
exports.findAll = (req, res) => {
  Deal.getAll((err, data) => {
    if (err)
      res.status(500).send({
        message:
          err.message || "Что-то случилось во время получения всех пользователей"
      });
    else 
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
    // я оставлю заголовки, получаемые с сервера, в таком виде, но конечно в реальном продакшене лучше переписать под конкретный origin
    // ну или вы делаете open API какой-нибудь, тогда делаете что хотите
    res.send(data);
  });
};


Остальную часть кода я запихну под

spoiler

, потому что тестировать мы будем только вышеупомянутые функции. Однако остальная часть кода тоже рабочая, но она технически отличается только аргументами от того, что уже было озвучено:

остальная часть кода

//  Найти одно дело по одному inner_id
exports.findOne = (req, res) => {
  Deal.findById(req.params.dealId, (err, data) => {
    if (err) {
      if (err.kind === "not_found") {
        res.status(404).send({
          message: `Нет дела с id ${req.params.dealId}.`
        });
      } else {
        res.status(500).send({
          message: "Проблема с получением пользователя по id"   req.params.dealId
        });
      }
    } else res.send(data);
  });
};

// Обновление пользователя по inner_id
exports.update = (req, res) => {
  // валидизируем запрос
  if (!req.body) {
    res.status(400).send({
      message: "Контент не может быть пустой"
    });
  }

// обновление дела по "айди" - на самом деле inner_key
  Deal.updateById(
    req.params.dealId,
    new Deal(req.body),
    (err, data) => {
      if (err) {
        if (err.kind === "not_found") {
          res.status(404).send({
            message: `Не найдено дело с id ${req.params.dealId}.`
          });
        } else {
          res.status(500).send({
            message: "Error updating deal with id "   req.params.dealId
          });
        }
      } else res.send(data);
    }
  );
};

// удалить дело по inner_key
exports.delete = (req, res) => {
  Deal.remove(req.params.dealId, (err, data) => {
    if (err) {
      if (err.kind === "not_found") {
        res.status(404).send({
          message: `Не найдено дело с ${req.params.dealId}.`
        });
      } else {
        res.status(500).send({
          message: "Не могу удалить дело с "   req.params.dealId
        });
      }
    } else res.send({ message: `дело было успешно удалено` });
  });
};

// Удалить все дела из таблицы
exports.deleteAll = (req, res) => {
  Deal.removeAll((err, data) => {
    if (err)
      res.status(500).send({
        message:
          err.message || "Что-то пошло не так во время удаления всех дел"
      });
    else res.send({ message: `Все дела успешно удалены` });
  });
};

  

Похожее:  Как парсером авторизироваться на сайте? — Хабр Q&A

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *