Чи слід абстрагувати запити бази даних із самої сторінки?


10

Під час написання генерації сторінок у PHP я часто опиняюсь як пишу набір файлів, залитих запитами до бази даних. Наприклад, у мене може бути запит, щоб отримати деякі дані про публікацію безпосередньо з бази даних для відображення на такій сторінці:

$statement = $db->prepare('SELECT * FROM posts WHERE id=:id');
$statement->bindValue(':id', $id, PDO::PARAM_INT);
$statement->execute();
$post = $statement->fetch(PDO::FETCH_ASSOC);
$content = $post['content']
// do something with the content

Ці швидкі разові запити, як правило, невеликі, але я іноді закінчую великими частинами коду взаємодії з базою даних, який починає виглядати досить безладним.

У деяких випадках я вирішив цю проблему, створивши просту бібліотеку функцій для обробки моїх db-запитів, пов’язаних з постами, скорочуючи цей блок коду до простого:

$content = post_get_content($id);

І це чудово. Або принаймні так, поки мені не потрібно буде робити щось інше. Можливо, мені потрібно отримати п'ять останніх публікацій для відображення в списку. Ну, я завжди можу додати ще одну функцію:

$recent_posts = post_get_recent(5);
foreach ($recent_posts as $post) { ... }

Але в кінцевому підсумку використовується SELECT *запит, який мені взагалі дійсно не потрібен, але часто занадто складний, щоб досить абстрактно. У кінцевому підсумку я отримую або велику бібліотеку функцій взаємодії з базами даних для кожного окремого випадку використання, або ряд брудних запитів всередині коду кожної сторінки. І навіть коли я побудую ці бібліотеки, я виявлю, що мені потрібно зробити одне крихітне приєднання, яке я раніше не використовував, і мені раптом потрібно написати ще одну високоспеціалізовану функцію, щоб виконати цю роботу.

Звичайно, я міг би використовувати функції для загальних випадків використання та запитів для конкретних взаємодій, але, як тільки я починаю писати необроблені запити, я починаю відступати у прямий доступ до всього. Або це, або я лінуся, і в будь-якому випадку я почну робити речі в петлях PHP, які дійсно слід робити безпосередньо в запитах MySQL.

Я хотів би запитати тих, хто має досвід написання інтернет-додатків: чи варто підвищити ремонтопридатність додаткових рядків коду та можливих неефективностей, які можуть ввести абстракції? Або просто використання прямих рядків запитів є прийнятним методом обробки взаємодій із базами даних?


Можливо, ви можете використовувати збережені процедури, щоб "обернути" безладні selects - si, вам доведеться викликати такі процедури лише з певними необхідними параметрами
k102

Відповіді:


7

Якщо у вас занадто багато функцій спеціалізованих запитів, ви можете спробувати розбити їх на складові біти. Наприклад

$posts = posts()->joinWithComments()->orderBy("post.post_date")->first(5);

Існує також ієрархія рівнів абстракції, які можуть вам бути корисними, щоб мати на увазі. Ти маєш

  1. mysql API
  2. ваші функції mysql, такі як select ("вибрати * з публікацій, де foo = bar"); або, можливо, більше композиційний якselect("posts")->where("foo = bar")->first(5)
  3. наприклад, функцій, специфічних для вашого домену програми posts()->joinWithComments()
  4. функції, характерні для певної сторінки, такі як commentsToBeReviewed($currentUser)

Це дуже платить за простоту обслуговування, щоб дотримуватися цього порядку абстракцій. Сценарії сторінок повинні використовувати лише функції рівня 4, функції рівня 4 повинні бути записані з точки зору функцій рівня 3 тощо. Це правда, що це займає трохи більше часу вперед, але це допоможе тримати ваші витрати на обслуговування постійними з часом (на відміну від "о, боже, вони хочуть чергових змін !!!"


2
+1 Цей синтаксис по суті створює власну ORM. Якщо ви дійсно переживаєте, що речі доступу до бази даних ускладнюються, і ви не хочете витрачати багато часу, цікавлячись деталями, я б запропонував використовувати зрілий веб-фреймворк (наприклад, CodeIgniter ), який уже розібрався в цьому. Або, принаймні, спробуйте його використати, щоб побачити, який синтаксичний цукор він дає вам, аналогічно тому, що продемонстрував xpmatteo.
Хартлі Броді

5

Розділення турбот - це принцип, про який варто прочитати, дивіться статтю у Вікіпедії.

http://en.wikipedia.org/wiki/Separation_of_concerns

Ще один принцип, про який варто ознайомитись - це з'єднання

http://en.wikipedia.org/wiki/Coupling_(computer_science )

У вас є дві чіткі проблеми: одне - це збір даних із бази даних, а друге - надання цих даних. У дійсно простих програмах, мабуть, мало про що турбуватися, ви щільно поєднали рівень доступу до бази даних та рівень управління зі своїм шаром візуалізації, але для невеликих додатків це не складно. Проблема полягає в тому, що веб-додатки мають тенденцію розвиватися, і якщо ви хочете коли-небудь збільшити масштаб веб-програми, будь-яким чином, тобто продуктивністю чи функціональністю, то у вас виникнуть деякі проблеми.

Скажімо, ви створюєте веб-сторінку коментарів, створених користувачем. Поруч йде гострий волохатий бос і просить вас почати підтримувати Native Apps, тобто iPhone / Android і т. Д. Нам потрібен вихід JSON, тепер вам потрібно витягнути код візуалізації, який генерував HTML. Після цього ви отримали бібліотеку доступу до даних з двома движками візуалізації і все в порядку, ви функціонально масштабували. Можливо, вам навіть вдалося зберегти все окремо, тобто логіку бізнесу від надання.

Поряд приходить бос і каже вам, що у нього є клієнт, який хоче відображати публікації на своєму веб-сайті, їм потрібен XML і їм потрібно близько 5000 запитів на секунду пікової продуктивності. Тепер вам потрібно генерувати XML / JSON / HTML. Ви можете відокремити рендерінг знову, як і раніше. Однак тепер вам потрібно додати 100 серверів, щоб зручно отримати необхідну продуктивність. Тепер ваша база даних потрапляє з 100 серверів, можливо, з десятками з'єднань на одному сервері, кожен з яких безпосередньо піддається трьом різним програмам з різними вимогами та різними запитами тощо. Доступ до бази даних на кожному фронтальному апараті є ризиком для безпеки та зростанням один, але я туди не піду. Тепер вам потрібно масштабувати ефективність, кожен додаток має різні вимоги до кешування, тобто різні проблеми. Ви можете спробувати і керувати цим в одному з щільно зв'язаних шарів, тобто до доступу до бази даних / логіки бізнесу / шару візуалізації. Занепокоєння кожного шару тепер починає перешкоджати один одному, тобто вимоги до кешування даних із бази даних можуть бути дуже різними, ніж рівень візуалізації, логіка, що у вас на бізнес-шарі, швидше за все, може перетікати в SQL, тобто рухатися назад, або він може кровоточити вперед в шар візуалізації, це одна з найбільших проблем, з якими я бачив, що все є в одному шарі, це як заливка залізобетону у ваше застосування, а не хорошим способом.

Існують стандартні способи підходу до таких типів питань, тобто HTTP-кешування веб-служб (squid / yts тощо). Кешування рівня додатків у самих веб-службах з чимось на кшталт запам’ятовуваного / переробленого. Ви також зіткнетеся з проблемами, коли ви почнете масштабувати свою базу даних, тобто кілька зчитуваних хостів і один головний або розподілений дані для хостів. Ви не хочете, щоб 100 хостів, які керували різними підключеннями до вашої бази даних, які відрізнялися залежно від запитів чи запиту чи читання, або в розділеній базі даних, якщо користувач “usera” вмикається в “[table / database] foo” для всіх запитів запису.

Розділення турбот - ваш друг, вибір, коли і де це робити - це архітектурне рішення і трохи мистецтво. Уникайте жорсткого з'єднання будь-чого, що розвиватиметься з дуже різними вимогами. Існує маса інших причин для того, щоб зберігати речі окремо, тобто це спрощує тестування, розгортання змін, безпеку, повторне використання, гнучкість тощо.


Я розумію, звідки ви приїжджаєте, і я не погоджуюся ні з чим, що ви сказали, але зараз це невелика проблема. Проект, про який йдеться, є особистим, і більшість моїх проблем з моєю нинішньою моделлю витікає з інстинкту мого програміста, щоб уникнути жорстких зв'язків, але я справді новачок у складній розробці на серверній стороні, тому кінець цього питання пішов трохи над моєю головою. І все-таки +1 за те, що мені здається гарною порадою, навіть якщо я не можу повністю дотримуватися цього проекту.
Олексій Король

Якщо те, що ти робиш, залишиться маленьким, то я б тримав це якомога простіше. Хороший принцип, якого слід дотримуватися тут, - YAGNI .
Гаррі

1

Я припускаю, що, коли ви говорите "сама сторінка", ви маєте на увазі вихідний файл PHP, який динамічно генерує HTML.

Не запитуйте базу даних і не генеруйте HTML в одному вихідному файлі.

Вихідний файл, де ви запитуєте базу даних, не є "сторінкою", хоча це вихідний файл PHP.

У вихідному файлі PHP, де ви динамічно створюєте HTML, ви просто здійснюєте дзвінки до функцій, визначених у вихідному файлі PHP, де доступ до бази даних.


0

Шаблон, який я використовую для більшості середніх проектів, є наступним:

  • Усі запити SQL розміщуються окремо від коду на стороні сервера, в окремому місці.

    Робота з C # означає використання часткових класів, тобто розміщення запитів в окремому файлі, враховуючи, що ці запити будуть доступні з одного класу (див. Код нижче).

  • Ці запити SQL є константами . Це важливо, оскільки це запобігає спокусі побудови SQL запитів на ходу (тим самим збільшуючи ризик ін'єкції SQL і в той же час ускладнюючи перегляд запитів пізніше).

Сучасний підхід у C #

Приклад:

// Demo.cs
public partial class Demo : DataRepository
{
    public IEnumerable<Stuff> LoadStuff(int categoryId)
    {
        return this
            .Query(Queries.LoadStuff)
            .With(new { CategoryId = categoryId })
            .ReadRows<Stuff>();
    }

    // Other methods go here.
}

public partial class Demo
{
    private static class Queries
    {
        public const string LoadStuff = @"
select top 100 [StuffId], [SomeText]
    from [Schema].[Table]
    where [CategoryId] = @CategoryId
    order by [CreationUtcTime]";

        // Other queries go here.
    }
}

Цей підхід корисний тим, що запити знаходяться в окремому файлі. Це дозволяє DBA переглядати та змінювати / оптимізувати запити та зобов’язувати модифікований файл керувати джерелом без конфлікту з зобов'язаннями, зробленими розробниками.

Пов'язана перевага полягає в тому, що джерело управління може бути налаштоване таким чином, щоб обмежити доступ DBA лише до тих файлів, які містять запити, і заборонити доступ до решти коду.

Чи можна це зробити в PHP?

PHP не вистачає як часткових класів, так і внутрішніх класів, так що, як це є, він не може бути реалізований у PHP.

Ви можете створити окремий файл із окремим статичним класом ( DemoQueries), що містить константи, враховуючи, що клас буде доступний звідусіль. Крім того, щоб уникнути забруднення глобальної області, ви можете розмістити всі класи запитів у спеціальному просторі імен. Це створить досить багатослівний синтаксис, але я сумніваюся, що ви можете уникнути багатослівності:

namespace Data {
    public class Demo inherit DataRepository {
        public function LoadStuff($categoryId) {
            $query = \Queries\Demo::$LoadStuff;
            // Do the stuff with the query.
        }

        // Other methods go here.
    }
}

namespace Queries {
    public static class Demo {
        public const $LoadStuff = '...';

        // Other queries go here.
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.