• Шаблоны для вашего сайта
  • Сделать сайт
  • Реклама
  • Berry CSS
  • Albireo Framework
  • Бесплатный HTML-курс
  • Telegram-канал
  • Обратная связь
MaxSite.org
О создании сайтов и не только
Создание сайтов под ключ (Украина) →
Вход
×
или зарегистрироваться

CodeIgniter 4. Работа с базой данных

CodeIgniterПросмотров: 8462Комментарии: 531 августа 2019 г.

Работа с базой данных всегда была сильной стороной CodeIgniter. В 4-й версии сохранился практически тот же самый подход, что и в прошлых версиях, хотя есть отличия. В первую очередь стоит отметить, что SQL Query Builder (Active Records в первой и второй версиях) теперь вынесен отдельным классом. Если раньше SQL-запрос строился прямо в объекте базы ($db), то сейчас это нужно делать отдельно.

Основной объект базы теперь содержит не так много методов, наиболее используемый из которых query().

Но самое главное это то, что CodeIgniter 4 поддерживает Named Bindings, что очень сильно его приближает к стандартной работе с PDO.

Для начала работы с базой следует создать базу любым способом, например с помощью phpMyAdmin. После этого нужно указать параметры доступа в конфигурации: файл app/Config/Database.php. Обратите внимание, что доступен параметр «DSN», что указывает на ещё один шаг к полноценному PDO.

Для тестирования мы пока просто создадим таблицу news и наполним её тестовыми данными в ручном режиме. Выполните SQL-запросы:

CREATE TABLE news (
        id int(11) NOT NULL AUTO_INCREMENT,
        title varchar(128) NOT NULL,
        slug varchar(128) NOT NULL,
        body text NOT NULL,
        PRIMARY KEY (id),
        KEY slug (slug)
); 

И записи:

INSERT INTO news VALUES
(1,'Elvis sighted','elvis-sighted','Elvis was sighted at the Podunk internet cafe. It looked like he was writing a CodeIgniter app.'),
(2,'Say it isn\'t so!','say-it-isnt-so','Scientists conclude that some programmers have a sense of humor.'),
(3,'Caffeination, Yes!','caffeination-yes','World\'s largest coffee shop open onsite nested coffee shop for staff only.');

Пропишем роутинг:

$routes->get('news', 'News::index');
$routes->get('news/(:segment)', 'News::page/$1');

То есть по адресу http://example.com/news будет выводится список всех записей. А по адресу http://example.com/news/SLUG — уже конкретная запись.

Начнём с модели.

В CodeIgniter для моделей предназначен каталог app/Models. Разместим в нём файл NewsModel.php:

namespace App\Models;
 
use CodeIgniter\Model;
 
class NewsModel extends Model
{
    protected $table = 'news';
    
    public function getNews($slug = false)
    {
        if ($slug === false)
            return $this->findAll();
		else
            return $this->asArray()->where(['slug' => $slug])->first();
    }
}

Основной метод getNews(), который возвращает набор записей: если $slug не указан, то возвращаются все записи, иначе только одна.

Поскольку мы наследуемся от системного класса CodeIgniter\Model, то используем его методы. Если сравнивать с предыдущими версиями фреймворка, системная модель сильно «разжирела»: в ней появилось много методов, полей, которые по задумке разработчиков должны упростить работу с базой.

Теперь делаем контролёр. Файл app/Controllers/News.php:

namespace App\Controllers;
 
use App\Models\NewsModel;
use CodeIgniter\Controller;
 
class News extends Controller
{
    public function index()
    {
        $model = new NewsModel();
        
        $data = [
            'news'  => $model->getNews(),
            'title' => 'All pages',
        ];
        
        echo view('news/header', $data);
        echo view('news/overview', $data);
        echo view('news/footer');
    }
    
    public function page($slug = NULL)
    {
        $model = new NewsModel();
        
        $page = $model->getNews($slug);
        
        if ($page)
        {
            $data['title'] = $page['title'];
            $data['page'] = $page;
        }
        else
        {
            $data['title'] = 'Page not found';
            $data['page']['title'] = 'Page not found';
            $data['page']['body'] = '404...';
        }
        
        echo view('news/header', $data);
        echo view('news/page', $data);
        echo view('news/footer');
    }
}

Методы index() и page() указаны в роутинге. Мы подключаем нашу NewsModel, получаем от неё данные и дальше формируем вывод.

Я специально оставил этот код приближенный к туториалу, поскольку такой подход наиболее часто и встречается при разработке, что создаёт ряд проблем.

Теперь сделаем «вьюшки». Все они в каталоге Views/news:

Файл header.php:

<!doctype html><html>
<head>
    <title><?= esc($title) ?></title>
</head>
<body>

Файл footer.php:

</body></html>

Файл page.php:

<h1><?= $page['title'] ?></h1>

<div><?= $page['body'] ?></div>

Файл overview.php:

<h1><?= $title ?></h1>

<?php if (!empty($news) and is_array($news)) : ?>
    <ul>
    <?php foreach ($news as $news_item): ?>
         <li><a href="<?= 'news/' . $news_item['slug'] ?>"><?= $news_item['title'] ?></a></li>
    <?php endforeach; ?>
    </ul>
<?php else : ?>
    <h3>No News</h3>
    <p>Unable to find any news for you.</p>
<?php endif ?> 

Вот, собственно и всё: набираем в браузере http://example.com/news и видим список ссылок записей.

Проблемы данного подхода

Первая проблема здесь в том, что системный класс Модели жестко завязан на базу данных. Это сильно противоречит концепции MVC, где модель должна инкапсулировать всю бизнес-логику, а не только работу с БД. Расширяя такую модель, мы тянем за собой очень много лишнего кода: посмотрите файл system/Model.php.

В методе нашего класса getNews() нет четкого понимания того, как он работает. Алгоритм функции должен быть ясным. Здесь же, вместо прямой работы с базой, используется прослойка через дополнительные методы. Возможно это и уменьшило размер кода, но не внесло в него ясности. Когда алгоритм выборки будет сложней, всё равно придётся использовать класс базы и эта прослойка окажется не нужной.

Что касается контролёра, то в нём модель используется только для получения выборки из базы. Дальше в контролёре выполняется ряд условий, поскольку выборка может оказаться пустой. Данные передаются в шаблонизатор, поэтому следует учесть этот момент. То есть контролёр взял на себя роль настоящей «модели» и от этого «распух». Когда логика простая и мало кода, это не является проблемой, но, как только код усложнится, то это приводит к ТТУК («Толстые, тупые, уродливые контроллеры»). Это фундаментальная ошибка, которая описыват работу с базой в туториале CodeIgniter с очень давних времён, и которая тиражируется большинством разработчиков.

Строго говоря, эта проблема не только в CodeIgniter, но в других php-фреймворках.

Ну и последняя проблема: как я указывал ранее — файлы модуля разбросаны по разным каталогам.

Нормальный вариант

К счастью CodeIgniter теперь позволяет следовать более-менее правильному подходу. Сделаем похожий пример на той же таблице.

Пропишем роутинг:

$routes->get('page', 'App\Modules\Page\Page::index');
$routes->get('page/(:segment)', 'App\Modules\Page\Page::showPage/$1');

Из него понятно, что все файлы модуля будут в одном каталоге app/Modules/Page. А адрес будет http://example.com/page

Начнем с контролёра. Файл app/Modules/Page/Page.php:

namespace App\Modules\Page;
 
class Page extends \CodeIgniter\Controller
{
	protected $view;
	protected $model;
	
	public function __construct()
	{
		$this->view = new View\View();
		$this->model = new Model\Model();
	}
	
	public function index()
	{
		$data = $this->model->getAllPages();
		$this->view->output('show-all', $data);
	}
	
	public function showPage($slug = '')
	{
		$data = $this->model->getPage($slug);
		$this->view->output('show-page', $data);
	}
}

Здесь мы используем свои модель и представление. Полученные данные от модели передаются в представление.

Файл представления app/Modules/Page/View/View.php:

namespace App\Modules\Page\View;
 
class View
{
	private $tmpl;
	
	public function __construct()
	{
		$this->tmpl = new \CodeIgniter\View\View('', APPPATH . 'Modules/Page/Layout/');
	}
	
	public function output($template, $data)
	{
		$this->tmpl->resetData();
		$this->tmpl->setData($data);
		
		echo $this->tmpl->render($template);
	}
}

Здесь мы подключаем системный класс шаблонизатора, где указываем, что файлы для парсинга будут находится в каталоге Layout.

Файл для вывода всех записей app/Modules/Page/Layout/show-all.php:

<!doctype html>
<html><head>
	<title><?= esc($title) ?></title>
</head>
<body>
	<h1><?= $header ?></h1>
	<ul>
	<?php
		foreach($pages as $page) {
			echo '<li><a href="page/' . $page['slug'] . '">' . $page['title'] . '</a></li>';
		}
	?>
	</ul>
</body>
</html>

И файл для вывода одиночной записи app/Modules/Page/Layout/show-page.php:

<!doctype html>
<html><head>
   <title><?= esc($page['title']) ?></title>
</head>
<body>
   <h1><?= $page['title'] ?></h1>
   <div><?= $page['body'] ?></div>
</body>
</html>

Теперь рассмотрим модель app/Modules/Page/Model/Model.php

namespace App\Modules\Page\Model;
 
class Model
{
	private $db;
	
	public function __construct()
	{
		$this->db = \Config\Database::connect();
	}
	
	public function getAllPages()
	{
		$query = $this->db->query('SELECT * FROM news');
		$pages = $query->getResultArray();
 
		$data['title'] = 'All Pages';
		$data['header'] = 'All Pages';
		$data['pages'] = $pages;
		
		return $data;
	}
	
	public function getPage($slug)
	{
		$query = $this->db->query('SELECT * FROM news WHERE slug = :slug:', ['slug' => $slug]);
		
		/* # либо вариант с SQL-Builder
		$builder = $this->db->table('news');
		$builder->select('*');
		$builder->where('slug', $slug);
		$query = $builder->get();
		*/
		
		$page = $query->getRowArray();

		if (empty($page)) {
			$page['title'] = 'Page not found';
			$page['body'] = '404...';
		}
		
		$data['page'] = $page;
		
		return $data;
	}
}

Важное отличие от предыдущего варианта в том, что здесь мы напрямую работаем с базой данных. На выходе уже готовый массив данных, который полностью годен для вывода через шаблонизатор. Логика, которая в предудущем примере была в контролёре, теперь, как и положено расположена в модели.

Запросы к базе выполняются с помощью метода query(). Для примера я разместил код для работы через SQL Builder.

Обратите внимание на то, как CodeIgniter использует именованные параметры в запросе: с обрамлением символом : с двух сторон. Это очень похоже на стандарт PDO, но не является им, поскольку в PDO следует использовать только один символ : с начала.

$sql = 'SELECT * FROM news WHERE slug = :slug';

Почему разработчики фреймворка сделали нестандартное решение, остаётся загадкой.


Создание сайтов (Украина) →
Обновление шаблона MF (сентябрь 2019)
Немного о своём PHP-фреймворке
twitter.com facebook.com
Другие записи сайта
Обработка исключений и ошибок в PHP
Обработка исключений и ошибок в PHP
Контекстные ссылки: за и против
Контекстные ссылки: за и против
Проблема View в MVC
Проблема View в MVC
CodeIgniter 4. PSR-4. Произвольная MVC
CodeIgniter 4. PSR-4. Произвольная MVC
Работа с куками в Alpine.js
Работа с куками в Alpine.js
Организация Less-файлов
Организация Less-файлов

Комментариев: 5 RSS

1Александр Соловей17-09-2019 10:19

Вопрос не по теме статьи, но по теме Codeigniter 4.

Напишите статью по работе с IncomingRequest Class.

Не совсем понятно в документации.

2MAX17-09-2019 10:36

Подумаю. Но вообще это по сути оболочка для функции parse_url() + пара «плюшек». :-)

3Георгий18-10-2019 10:53

Кстати, любые вопросы можно задавать на форуме самим разработчикам Codeigniter4.

https://forum.codeigniter.com/forum-28.html

Их немного и, как правило, они оперативно отвечают.

4Дмитрий01-10-2020 17:02

А когда будет продолжение ? Я статью прочитал сразу как Вы её опубликовали. И вот уже больше года жду продолжения. Думаю и н6е я один. Расчитываете ли вообще продолжать ? Хорошего материала по CodeIgniter и так не найти...

5Дмитрий30-01-2021 20:31

Спасибо за ответ. Очень понятный и всеобъемлящий.

Оставьте комментарий!

Комментарий будет опубликован после проверки. Вы соглашаетесь с правилами сайта.

(обязательно)

Навигация
  • Шаблоны для MaxSite CMS 22
  • jQuery и JavaScript 6
  • Java и Android 5
  • PHP/ООП 25
  • SQL 17
  • Albireo Framework 11
  • Berry CSS 7
  • CSS, HTML, LESS, SASS 23
  • PHP 37
  • Тайм-менеджмент 9
  • Софт 37
  • SEO 13
  • Git. GitHub 3
  • CodeIgniter 5
  • Landing Page 3
  • Alpine.js 14
  • Фильмы 2
  • Дневник 55

Здесь можно заказать создание сайта (только Украина), шаблона или лендинга. Также вы можете выбрать готовые шаблоны для MaxSite CMS по небольшой цене. Также можно купить отдельные модули, компоненты для вашего сайта.

MaxSite.org
Как создать свой сайт

Услуги по созданию сайтов, блогов, лендингов
Обратная связь • Реклама на сайте
Карта сайта
Мои проекты
  • Шаблоны для вашего сайта
  • Заказать создание сайта
  • MaxSite CMS
  • Berry CSS (CSS Utilities)
  • Albireo Framework
  • UniCSS (Universal Atomic CSS)
  • Landing Page Framework
  • Бесплатные НТML-курсы
Ссылки
  • Telegram-канал
  • Github
  • Twitter
  • Telegram-бот
  • RSS
© MaxSite.org, 2006-2022. Работает на MaxSite CMS | Время: 0.3135 | SQL: 20 | Память: 4.64MB | Вход