Как создать доску объявлений на CodeIgniter 4.
Сразу скажу ,что исходный код того, что получилось в итоге, я опубликовал на github: https://github.com/dXdYdZOlegKubanov/CodeIgneterSimpleAds
А теперь приступим.
1 Качаем FrameWork
2 Создаём виртуальный домен на сервере. Я назвал его “board”.
3 Распаковываем фреймворк
4 Задаем корневой папкой папку public. В случае использования OpenServer это делается на отдельной вкладке панели настроек.
5 Проектируем базу данных
На доске объявлений планируется наличие категорий и объявлений в категориях. Следовательно, должны присутствовать таблица категорий и таблица объявлений
таблица categories
id — integer primary_key
parent_id — integer index
name — varchar(255)
таблица ads
id — integer primary_key
category_id — integer index
title — varchar(255)
text — TEXT
Создаем БД и настраиваем подключение к ней. Базу данных я создал с помощью приложения HeidiSQL. Кодировку для нее выставил utf8_general_ci. В app/config/database.php в массиве $default выставляем поля:
'username' => 'логин',
'password' => 'пароль',
'database' => 'ads',
В значения полей вписываем логин и пароль для доступа к базе данных.
Выставляем в CodeIgniter режим разработки для отображения ошибок. Для этого переименовываем файл “env” в “.env” Затем выставляем в нем строку
CI_ENVIRONMENT = development
После этого начнут отображаться сообщения об ошибках.
Заходим на главную страницу проекта и видим сообщение, что запрещено использование функции putenv. Удаляем её из запрещенных в файле настроек php (обычно он называется php.ini). Теперь на главной отображается такое сообщение
The framework needs the following extension(s) installed and loaded: intl
Значит, надо установить это расширение. Для установки раскомментируем строку extension = intl в файле настроек php. Снова обновляем главную страницу и видим приветствие. Значит, она заработала.
6 Создаем миграции
Миграция для объявлений, пишем её в файл 2021_07_16_000001_add_ads.php
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddAds extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true
],
'category_id' => [
'type' => 'INT',
'unsigned' => true,
'index' => true
],
'title' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => false
],
'text' => [
'type' => 'TEXT',
'constraint' => 65535,
'null' => false
]
]);
$this->forge->addKey('id', true);
$this->forge->createTable('ads');
}
public function down()
{
$this->forge->dropTable('ads');
}
}
Миграция для категорий, — в файл 2021_07_16_000001_add_categories.php :
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddCategories extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true
],
'parent_id' => [
'type' => 'INT',
'unsigned' => true,
'index' => true,
'null'=>true
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => false
]
]);
$this->forge->addKey('id', true);
$this->forge->createTable('categories');
}
public function down()
{
$this->forge->dropTable('categories');
}
}
В этих миграциях сначала задается список колонок таблицы, потом добавляется первичный ключ этой строкой:
$this->forge->addKey('id', true);
А затем создаётся таблица.
7 Создаём наполнители таблиц данными
8 Запускаем миграции и наполнители. Для этого можно создать специальный контроллер:
9 Пишем модель для категорий
<?php
namespace App\Models;
use CodeIgniter\Model;
class CategoriesModel extends Model
{
protected $table = 'categories';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $allowedFields = ['parent_id', 'name'];
protected $useTimestamps = false;
protected $createdField = '';
protected $updatedField = '';
protected $deletedField = '';
protected $validationRules = [
'parent_id' => 'required|integer|is_not_unique[categories.id,id,{parent_id}]',
'name' => 'required|max_length[255]'];
protected $validationMessages = [];
protected $skipValidation = false;
}
10 Пишем модель для объявлений
<?php
namespace App\Models;
use CodeIgniter\Model;
class AdsModel extends Model
{
protected $table = 'ads';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $allowedFields = ['category_id', 'title', 'text'];
protected $useTimestamps = false;
protected $createdField = '';
protected $updatedField = '';
protected $deletedField = '';
protected $validationRules = [
'category_id' => 'required|integer|is_not_unique[categories.id,id,{category_id}]',
'title' => 'required|max_length[255]',
'text' => 'required|max_length[65535]'];
protected $validationMessages = [];
protected $skipValidation = false;
}
Это стандартные шаблоны моделей codeIgniter, уже поддерживающие операции CRUD (create, remove, update, delete).
11 Пишем контроллер для объявлений
class Ads extends BaseController
{
public function index($category_id=false)
{
//Создаем объект модели
$adsModel = new \App\Models\AdsModel();
//Если задана категория, то добавляем ее в условия
if($category_id!==false)
$adsModel->where('category_id',$category_id);
//Получаем список объявлений
$ads=$adsModel->findAll();
//Отправляем объявления в вид и отображаем страницу
echo view('ads/list',['ads'=>$ads,'category_id'=>$category_id]);
}
}
Создаем вид для списка объявлений
<div id='ads'>
<?php foreach($ads as $ad): ?>
<div class='ad'>
<h2><?=$ad['title']?></h2>
<p><?=$ad['text']?></p>
</div>
<?php endforeach; ?>
</div>
Тут в цикле перебирается массив объявлений, и для каждого из них выводится отдельный блок div, в котором внутри заголовка (h2) отображается заголовок объявления, а внутри текста (p) — его текст.
12 Проверяем работу списка. Для этого в браузере заходим по адресу http://board/index.php/ads
При вводе адреса с заданием категории выводятся только два поста из этой категории
http://board/index.php/ads/index/1
Чтобы такие адреса были без index.php, а выглядели так:
Выяснилось, что надо в файле app/Config/App.php задать параметр public $indexPage = '', убрав оттуда index.php, а также в public/.htaccess закомментировать строку “RewriteBase /public”.
13 Теперь сделаем ссылку для добавления объявления в категорию. Для этого разместим ее в шаблоне списка объявлений
<?php if($category_id!==false): ?>
<div class='controls'>
<a href="/ads/add/<?=$category_id?>">Добавить объявление в выбранную категорию</a>
</div>
<?php endif; ?>
Это ссылка на действие add контроллера ads с передачей туда идентификатора категории, в которую будет выполняться добавление.
Затем, создадим в контроллере объявлений Ads действие add для выполнения добавления объявления.
public function add($category_id)
{
//Если отображаем страницу для добавления объявления и ещё не выполнено сохранение
if($this->request->getMethod()!=="post")
{
echo view('ads/add',['category_id'=>$category_id]);
return true;
}
//Сюда попадаем, если уже нажали кнопку "Сохранить"
$model = new \App\Models\AdsModel();
$saveData=$this->request->getPost();
//Добавляем в данные для сохранения идентификатор категории
//if($category_id!==false)
$saveData['category_id']=$category_id;
if($model->save($saveData)===false)
//Если сохранение прошло не удалось, то отображаем ошибки
echo view('ads/add',['errors'=>$model->errors(),'category_id'=>$category_id,'data'=>$saveData]);
else
//Если сохранено успешно, то отображаем сообщение об успешном сохранении
echo view('ads/add_success');
}
Теперь список объявлений при выбранной категории выглядит так:
Страница добавления объявления выглядит так:
14 Поменяем данные в БД. Чтоб было повеселее. Для этого создадим новые наполнители данными.
Для категорий:
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class Categories extends Seeder
{
public function run()
{
$data = [
[
'parent_id' => 'null',
'name' => 'Автомобили'
],
[
'parent_id' => 'null',
'name' => 'Мотоциклы'
]
];
foreach($data as $d)
{
//$this->db->query("INSERT INTO users (username, email) VALUES(:username:, :email:)", $d);
// Using Query Builder
$this->db->table('categories')->insert($d);
}
}
}
И для объявлений:
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class Ads extends Seeder
{
public function run()
{
$data = [
[
'category_id' => 1,
'title' => 'Продам Таврию',
'text' => 'Неплохой тарантас'
],
[
'category_id' => 1,
'title' => 'Продам Жигули',
'text' => 'Помощнее, чем Таврия'
],
[
'category_id' => 1,
'title' => 'Продам Москвич',
'text' => 'Помощнее, чем Таврия но постарше, чем Жигули'
],
[
'category_id' => 2,
'title' => 'Продам мотоцикл Иж Юпитер-5',
'text' => 'Когда-то он был в хорошем состоянии'
],
[
'category_id' => 2,
'title' => 'Продаётся мотоцикл "Урал"',
'text' => 'Мощный двигатель и вал, но меня он задолбал'
],
];
/*foreach($data as $d)
{
//$this->db->query("INSERT INTO users (username, email) VALUES(:username:, :email:)", $d);
// Using Query Builder
$this->db->table('users')->insert($d);
}*/
$this->db->table('ads')->insertBatch($data);
}
}
15 Меняем страницу по умолчанию. Для этого в файле app/Config/Routes.php вместо
$routes->get('/', 'Home::index');
пишем
$routes->get('/', 'Ads::index');
16 Теперь сделаем шаблон всего сайта. Для этого будем использовать Bootstrap. Создадим два шаблона - header.php и footer.php.
header.php:
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>Учебная доска объявлений</title>
</head>
<body>
<h1>Учебная доска объявлений</h1>
<!-- Optional JavaScript; choose one of the two! -->
<!-- Option 1: Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<div class="container">
<div class="row">
<div class="col-sm-3">
<?php /* var_dump($categories);*/ ?>
Категории:
<ul class="list-unstyled fw-normal pb-1 medium">
<?php foreach($categories as $category): ?>
<li><a class='d-inline-flex align-items-center rounded' href='/ads/index/<?=$category['id']?>'><?=$category['name']?></a></li>
<?php endforeach; ?>
</ul>
</div>
<div class="col">
footer.php:
Кроме непосредственно шапки, а также подключения стилей и скриптов bootstrap, тут находится отображение меню категорий в цикле.
Также, добавим в конструктор функцию инициализации, и в ней будем получать пункты меню из модели:
private $categoriesModel;
//Текущий список категорий для отображения
private $categories;
public function initController($request, $response, $logger)
{
// Do Not Edit This Line
parent::initController($request, $response, $logger);
//--------------------------------------------------------------------
// Preload any models, libraries, etc, here.
//--------------------------------------------------------------------
// E.g.: $this->session = \Config\Services::session();
//Создаем объект модели категорий и сохраняем его в пеперенную объекта контроллера
$this->categoriesModel=new \App\Models\CategoriesModel();
$this->categories=$this->categoriesModel->find();
}
Тут мы создаем в контроллере два приватных свойства — для хранения модели категорий и самого их списка. В предусмотренной фреймворком функции инициализации контроллера initController, после вызова родительской, создаём объект модели категорий и получаем их список, сохраняя его в свойство контроллера $this->categories.
В контроллер объявлений добавим новую функцию для отображения шаблона. Сделаем её приватной, чтобы это не было действием:
private function display($template,$data)
{
echo view('header',['categories'=>$this->categories]);
echo view($template,$data);
echo view('footer');
}
Первая строка запускает отображение шапки шаблона для всего сайта, вторая - основного шаблона, третья - нижней части страницы.
В действиях контроллера меняем echo view(... на $this->display(...
Итого, получили вот это:
Немного улучшаем внешнее оформление с помощью классов Bootstrap и просто стилей CSS, и получаем это:
Комментарии
Отправить комментарий