MVC приложение на php. Часть 6. crud, mapping
В этой статье сделаем админку полезной — она сможет создавать статьи, категории, редактировать, удалять. А также привяжем статьи к категориям.
Плодить контроллеры не будем, все добавим в 2 базовых.
Итак, форма для ред-ия та же что для просмотра, соот-но экшн будет одинаковый. Принимаем post данные
application/controllers/admin/articles.php
/**
*
*/
public function wrap_images(&$item, $key){
$item = "<img src='images/$item' alt='' class='preview'>";
}
/**
* @param array $args
*/
function article(array $args = null){
$model = new Articles1();
if(!empty($_POST)){
/* base values */
$data = [];
foreach ($_POST as $key => $value) {
global $data;
if(!empty($value)){
$data[$key] = $value;
}
}
/* image */
if(!empty($_FILES['image']['name'][0])){
$data["preview_pic"] = $_FILES['image']['name'][0];
$uploaded_files = [];
foreach($_FILES['image']['error'] as $key=> $error){
global $uploaded_files;
if($error == UPLOAD_ERR_OK){
$tmp_name = $_FILES['image']['tmp_name'][$key];
$name = basename($_FILES['image']['name'][$key]);
$res = move_uploaded_file($tmp_name, PATH_SITE.DS.'images'.DS.$name);
if($res){
$uploaded_files[] = $_FILES['image']['name'][$key];
}
}
}
// массив картинок переводим в строку
$data["image"] = implode(',' , $uploaded_files);
}
$res = $model->edit($data);
echo $res;
}
$id = (isset($args[0])? $args[0]: "");
$article = $model->getById($id);
// строку преобразуем в массив и оборачиваем тегами
if(!empty($article['image'])){
$arr = explode(',' , $article['image']);
array_walk($arr, [$this , 'wrap_images']);
$article['image'] = implode('
' ,$arr);
}
$data = array(
'article' => $article,
);
$this->view->generate('', $data);
}
И метод для записи в БД
application/models/admin/articles.php
/**
*
*/
public function edit($data = null){
if(!empty($data)){
$sql_articles = 'UPDATE article SET';
$where = 'WHERE id='.$data['id'];
$result = DateBase::build_query($sql_articles, $data, $where);
return $result;
}
}
Во вьюхе неболшие правки.
<form action="/admin/articles/article/<?= $article['id'] ?>" method="post" name="form1" enctype="multipart/form-data">
<input type="hidden" name="id" value="<?= $article['id'] ?>">
Для клиентcкой части добавим в контроллере преобразование картинок в теги, и заменим в представлении вывод.
application/controllers/client/articles.php
/**
*
*/
public function wrap_images(&$item, $key){
$item = "<img src='images/$item' alt='' class='preview'>";
}
// строку преобразуем в массив и оборачиваем тегами
if(!empty($article['image'])){
$arr = explode(',' , $article['image']);
array_walk($arr, [$this , 'wrap_images']);
$article['image'] = implode('' ,$arr);
}
// view
<?=$article['image']?>
Поле категорий пока пустое.
Добавлю экшн для добавления статьи. Он практически полностью копирует функционал редактирования.
/**
*
*/
public function add(){
$model = new Articles1();
if(!empty($_POST)){
/* base values */
$data = [];
foreach ($_POST as $key => $value) {
global $data;
if(!empty($value)){
$data[$key] = $value;
}
}
/* image */
if(!empty($_FILES['image']['name'][0])){
$data["preview_pic"] = $_FILES['image']['name'][0];
$uploaded_files = [];
foreach($_FILES['image']['error'] as $key=> $error){
global $uploaded_files;
if($error == UPLOAD_ERR_OK){
$tmp_name = $_FILES['image']['tmp_name'][$key];
$name = basename($_FILES['image']['name'][$key]);
$res = move_uploaded_file($tmp_name, PATH_SITE.DS.'images'.DS.$name);
if($res){
$uploaded_files[] = $_FILES['image']['name'][$key];
}
}
}
// массив картинок переводим в строку
$data["image"] = implode(',' , $uploaded_files);
}
$res = $model->add($data);
}
$data = array();
$this->view->generate('', $data);
}
Модель для добавления статьи
/**
*
*/
public function add($data = null){
if(!empty($data)){
$sql_articles = 'INSERT INTO article SET';
$result = DateBase::build_query($sql_articles, $data);
return $result;
}
}
Вьюха тоже как в редактировании.
application/views/admin/addArticle.php
<!-- Left col -->
<section class="col-lg-10 connectedSortable">
<!-- quick email widget -->
<div class="box box-info">
<div class="box-header">
<h3 class="box-title">Добавить статью</h3>
<!-- tools box -->
<div class="pull-right box-tools">
<button class="btn btn-info btn-sm" data-widget="remove" data-toggle="tooltip" title="Remove"><i
class="fa fa-times"></i></button>
</div>
<!-- /. tools -->
</div>
<div class="box-body">
<!-- //////////////////////////////// form ////////////////////////////// -->
<form action="/admin/articles/add" method="post" name="add_article" enctype="multipart/form-data">
<div class="form-group">
<input type="text" class="form-control" name="title" placeholder="Название статьи"/>
</div>
<!-- datepicker -->
<div class="form-group">
<label>Дата:</label>
<div class="input-group">
<div class="input-group-addon">
<i class="fa fa-calendar"></i>
</div>
<input type="text" class="form-control pull-right" id="datepicker" name="date">
</div>
<!-- /.input group -->
</div>
<!-- anons -->
<div class="form-group">
<label for="anons">Анонс</label><br/>
<textarea name="anons" id="anons"></textarea>
</div>
<!-- text description -->
<div class='box'>
<div class='box-header'>
<h3 class='box-title'>Текст статьи</h3>
<!-- tools box -->
<div class="pull-right box-tools">
<button class="btn btn-default btn-sm" data-widget='collapse' data-toggle="tooltip"
title="Collapse"><i class="fa fa-minus"></i></button>
<button class="btn btn-default btn-sm" data-widget='remove' data-toggle="tooltip"
title="Remove"><i class="fa fa-times"></i></button>
</div>
<!-- /. tools -->
</div>
<!-- /.box-header -->
<div class='box-body pad'>
<div>
<textarea name="description" class="textarea" id="editor1"
placeholder="Place some text here"
style="width: 100%; height: 200px; font-size: 14px; line-height: 18px; border: 1px solid #dddddd; padding: 10px;"></textarea>
</div>
</div>
</div>
<!-- Select multiple-->
<div class="form-group">
<label>Выберите категорию:</label>
<select multiple class="form-control" name="category[]">
<? foreach ($data['categories'] as $categories): ?>
<option value="<?= $categories['id'] ?>"><?= $categories['name'] ?></option>
<? endforeach ?>
</select>
</div>
<div class="form-group">
<label for="exampleInputFile">Загрузка файлов</label>
<input name="image[]" type="file" multiple="multiple" id="exampleInputFile">
<p class="help-block">Выберите файлы для загрузки</p>
</div>
<div class="box-footer clearfix">
<button type="submit" class="pull-right btn btn-default" name="send">Добавить <i
class="fa fa-arrow-circle-right"></i></button>
</div>
</form>
<!-- //////////////////////////////// end form ////////////////////////////// -->
</div>
</div>
</section><!-- /.Left col -->
Остался экшн для удаления статьи.
/**
*
*/
public function delete($args = null){
$id = (isset($args[0])? $args[0]: "");
if($id){
$model = new Articles1();
$article = $model->getById($id);
$res = $model->delete($id);
// удаляем картинки
if($res == true){
$arr = explode(',' , $article['image']);
array_walk($arr, [$this, 'delete_images']);
}
}
$this->view->redirect('/admin');
}
/**
*
*/
public function delete_images($item){
if(file_exists(PATH_SITE.DS.'images'.DS.$item)){
unlink(PATH_SITE.DS.'images'.DS.$item);
}
}
Удалим из БД
/**
*
*/
public function delete($id = null){
if(!empty($id)){
$sql = 'DELETE FROM article WHERE id = %s';
$result = DateBase::query($sql, $id);
}
}
Вид — это иконка удаления в таблице
<td>
<a href="/admin/articles/delete/<?= $article['id'] ?>" class="delete_item"
data-confirm-title="Удалить запись?"
data-confirm-message="Вы действительно хотите удалить эту запись?">
<span class="glyphicon glyphicon-trash"></span>
</a>
</td>
Для подтверждения добавил мод. окно от бутстрап
<script src="js/confirm-bootstrap.js"></script>
// app.js
$(".delete_item").confirmModal();
Категории. CRUD
Поработаем с категориями. Все практич. тож самое, даже попроще, не будет возни с картинками.
application/controllers/admin/categories.php
if(!empty($_POST)){
/* base values */
$data = [];
foreach ($_POST as $key => $value) {
global $data;
if(!empty($value)){
$data[$key] = $value;
}
}
$res = $model->edit($data);
}
Апдейт в БД
application/models/admin/categories.php
/**
*
*/
public function edit($data = null){
if(!empty($data)){
$sql_articles = 'UPDATE category SET';
$where = 'WHERE id='.$data['id'];
$result = DateBase::build_query($sql_articles, $data, $where);
return $result;
}
}
Небольшие правки в представлении
<form action="/admin/categories/category/<?= $category['id'] ?>" method="post">
<input type="hidden" name="id" value="<?= $category['id'] ?>">
Добавление категории
/**
*
*/
public function add(array $args = null){
$model = new categories1();
$id = (isset($args[0])? $args[0]: "");
if(!empty($_POST)){
/* base values */
$data = [];
foreach ($_POST as $key => $value) {
global $data;
if(!empty($value)){
$data[$key] = $value;
}
}
$res = $model->add($data);
}
$data = array();
$this->view->generate('addCategory', $data);
}
Model
/**
*
*/
public function add($data = null){
if(!empty($data)){
$sql_articles = 'INSERT INTO category SET';
$result = DateBase::build_query($sql_articles, $data);
return $result;
}
}
Вид
<section class="col-lg-7 connectedSortable">
<!-- quick email widget -->
<div class="box box-info">
<div class="box-header">
<h3 class="box-title">Добавить категорию</h3>
<!-- tools box -->
<div class="pull-right box-tools">
<button class="btn btn-info btn-sm" data-widget="remove" data-toggle="tooltip" title="Remove"><i
class="fa fa-times"></i></button>
</div>
<!-- /. tools -->
</div>
<div class="box-body">
<form action="/admin/categories/add/" method="post">
<div class="form-group">
<input type="text" class="form-control" name="name" placeholder="Название категории" value="" />
</div>
<div>
<textarea name="description" class="textarea" placeholder="Описание "
style="width: 100%; height: 125px; font-size: 14px; line-height: 18px; border: 1px solid #dddddd; padding: 10px;"></textarea>
</div>
<div class="box-footer clearfix">
<button class="pull-right btn btn-default" type="submit" name="send">Добавить <i
class="fa fa-arrow-circle-right"></i></button>
</div>
</form>
</div>
</div>
</section><!-- /.Left col -->
Удаление категории
/**
*
*/
public function delete($args = null){
$id = (isset($args[0])? $args[0]: "");
if($id){
$model = new categories1();
$res = $model->delete($id);
}
$this->view->redirect('/admin/categories');
}
Запрос одинаков с articles кроме названия таблицы
/**
*
*/
public function delete($id = null){
if(!empty($id)){
$sql = 'DELETE FROM category WHERE id = %s';
$result = DateBase::query($sql, $id);
return $result;
}
}
Вид добавляем иконку с модалкой
<td>
<a href="/admin/categories/delete/<?= $categories['id'] ?>" class="delete_item"
data-confirm-title="Удалить запись?"
data-confirm-message="Вы действительно хотите удалить эту запись?">
<span class="glyphicon glyphicon-trash"></span>
</a>
</td>
Mapping
Создадим отношение многие к одному. Для этого создадим отд. таблицу category_article
articles___(20-37-36_06-03-2017)__rand945344.sql
CREATE TABLE `category_article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`category_id` int(11) DEFAULT NULL,
`article_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `article_id` (`article_id`),
KEY `category_id` (`category_id`),
CONSTRAINT `category_article_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
CONSTRAINT `category_article_ibfk_2` FOREIGN KEY (`article_id`) REFERENCES `article` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 AVG_ROW_LENGTH=1170;
При удалении статьи или категории удаление будет каскадно.
Добавим список категорий на стр. редакти-я статьи. Для этого в контроллере нужно будет обращение к модели категорий. Напишем автолоад.
function __autoload($class_name)
{
define('APP_PATH', 'application');
$path = strtolower(str_replace("\\", DS, $class_name));
if (file_exists(PATH_SITE.DS.APP_PATH.DS.$path . ".php")) {
include_once(PATH_SITE.DS.APP_PATH.DS.$path . ".php");
}else {
header("HTTP/1.0 404 Not Found");
echo "К сожалению такой страницы не существует. " .PATH_SITE.DS.APP_PATH.DS.$path . ".php ";
exit;
}
}
Выведем селект. Получим массив и преобразуем в теги.
use Models\Admin\Categories;
$categories_mod = new Categories();
$categories = $categories_mod->get_categories();
// категории преобразуем в селект
if(!empty($categories)){
array_walk($categories, [$this, 'categories_select']);
$cats = "<select name='category[$id]' multiple='multiple' class='form-control'>";
$cats .= implode('', $categories);
$cats .= "</select>";
}
/**
*
*/
public function categories_select(&$item, $key){
$item = "<option value='{$item['id']}'>{$item['name']}</option>";
}
Возни с циклами во вьюхе не будет
<?=$categories;?>
Обработаем значения категорий
// категории
$category = [];
if(!empty($_POST['category'])){
foreach ($_POST['category'] as $key => $value) {
global $category;
$category[$key]['article_id'] = $id;
$category[$key]['category_id'] = $value;
}
}
$model->edit($data, $category);
/**
*
*/
public function edit($data = null, $category = null){
if(!empty($data)){
$sql_articles = 'UPDATE article SET';
$where = 'WHERE id='.$data['id'];
$result = DateBase::build_query($sql_articles, $data, $where);
// return $result;
}
if(!empty($category)){
foreach($category as $cat){
$sql_articles = 'INSERT INTO category_article SET';
$result = DateBase::build_query($sql_articles, $cat);
}
}
}
Выведем результаты в списке статей. Поменяем запрос к табл. статей.
/**
*
*/
public function get_articles(){
$sql = 'SELECT a.*, GROUP_CONCAT(DISTINCT c.name )as category
FROM article a
LEFT JOIN category_article ca
ON a.id = ca.article_id
LEFT JOIN category c
ON c.id = ca.category_id
GROUP BY a.id';
$result = DateBase::query($sql);
$row2 = array();
while ($row = $result->fetch_assoc()) {
array_push($row2 , $row);
}
if(empty($row2))return;
return $row2;
}
Вид просто
<th>Категории</th>
<td><?= $article['category'] ?></td>
Осталось связывать категории при добавлении статьи
// категории
$category = [];
if(!empty($_POST['category'])){
foreach ($_POST['category'] as $key => $value) {
global $category;
$category[$key]['category_id'] = $value;
}
}
$res = $model->add($data, $category);
Как видишь тут id сатьи пока нет, она новая, получаем ее по insert_id
/**
*
*/
public function add($data = null, $category = null){
if(!empty($data)){
$sql_articles = 'INSERT INTO article SET';
$result = DateBase::build_query($sql_articles, $data);
}
$id = DateBase::insert_id();
if(!empty($category) && isset($id)){
foreach($category as $key=>$cat){
$cat['article_id'] = $id;
$sql_articles = 'INSERT INTO category_article SET';
$result = DateBase::build_query($sql_articles, $cat);
}
}
}
Осталось все это добро в клиентской части вывести. По сути работа на копипаст.
Конечно же, функционал админки не самый полный. При редактировании надо категории перезаписывать, а не просто добавлять. Выбранные категории должны отмечаться в селекте. Картинки надо чтобы удалялись ненужные. Все это сделать не сложно.
Leave a comment