Как создать доску объявлений на 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, а выглядели так:


http://board/ads/index/1


Выяснилось, что надо в файле 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:

</div>
</div>
</div>
  </body>
</html>

Кроме непосредственно шапки, а также подключения стилей и скриптов 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, и получаем это:




Готовый исходный код я опубликовал на Github: https://github.com/dXdYdZOlegKubanov/CodeIgneterSimpleAds

Комментарии

Популярные сообщения из этого блога

Почему не работает header('Location: site.com') в PHP-скрипте