Следующая статья представляет собой выдержку из PHP и MySQL: от новичка до ниндзя,
Настройка среды разработки PHP с помощью Docker
Руководство для начинающих по PHP
Знакомство с MySQL: руководство для начинающих
Отображение данных из MySQL в Интернете: введение
Вот и все, на что вы подписались! В этой главе вы узнаете, как брать информацию, хранящуюся в базе данных MySQL, и отображать ее на
Итак, вы написали свой первый
Теперь вы готовы научиться использовать эти инструменты вместе для создания
Примечание: как и в главе 3, здесь я использую «MySQL» для обозначения протокола базы данных. Ваши
Большая картинка
Прежде чем мы прыгнем вперед, стоит сделать шаг назад, чтобы получить четкое представление о нашей конечной цели. В нашем распоряжении есть два мощных инструмента: язык сценариев PHP и механизм базы данных MySQL. Важно понять, как они будут сочетаться друг с другом.
Целью использования MySQL для нашего
Отношения между
Как показано на изображении выше, язык сценариев PHP является посредником, говорящим на обоих языках. Он обрабатывает запрос страницы и извлекает данные из базы данных MySQL, используя
Просто чтобы это было ясно и свежо в вашей памяти, вот что происходит, когда на странице вашего
Программное обеспечение
Некоторые команды PHP (которые будут в центре внимания этой главы) подключаются к базе данных MySQL и запрашивают контент, принадлежащий
База данных MySQL отвечает, отправляя запрошенный контент сценарию PHP.
Сценарий PHP сохраняет содержимое в одну или несколько переменных PHP, а затем использует echoоператоры для вывода содержимого как части
Интерпретатор PHP завершает работу, передавая копию созданного им
Создание учетной записи пользователя MySQL
Чтобы PHP мог подключиться к вашему серверу базы данных MySQL, ему нужно будет использовать имя пользователя и пароль. Пока что все, что содержит ваша база данных шуток, — это несколько содержательных острот, но вскоре она может содержать конфиденциальную информацию, такую как адреса электронной почты и другие личные данные о пользователях вашего сайта. По этой причине MySQL спроектирован так, чтобы быть очень безопасным, предоставляя вам жесткий контроль над тем, какие соединения он будет принимать и что этим соединениям разрешено делать.
Среда Docker уже содержит пользователя MySQL в главе 3, которого вы уже использовали для входа на сервер MySQL.
Вы можете подключиться к базе данных из своего
Вы должны создать новую учетную запись пользователя только с определенными привилегиями, необходимыми для работы с ijdbбазой данных, от которой зависит ваш
Чтобы создать пользователя, откройте MySQL Workbench и подключитесь к своему серверу. Затем выполните следующие запросы:
CREATE USER 'ijdbuser’@'%' IDENTIFIED BY 'mypassword’;
GRANT ALL PRIVILEGES ON `ijdb`. * TO 'ijdbuser’@'%';
Первый запрос говорит сам за себя: он создает пользователя ijdbuserс паролем mypassword. Знак%после имени пользователя указывает, что к базе данных можно подключиться из любого места. Второй запрос дает пользователю полный доступ к ijdbсхеме, в результате этот пользователь может видеть и изменять все таблицы, столбцы и данные в ijdbсхеме, но не имеет доступа ни к чему за ее пределами.
Теперь, когда пользователь ijdbuserсоздан, мы можем использовать его для подключения к базе данных. С этим пользователем можно установить соединение в MySQL Workbench, но, поскольку права доступа ограничены, лучше оставить MySQL Workbench с использованием v.jeучетной записи. Вместо этого мы будем использовать нового пользователя при подключении из
Подключение к MySQL с помощью PHP
Прежде чем вы сможете извлекать контент из базы данных MySQL для включения в
Хотя в этой главе речь идет исключительно о подключении к MySQL из PHP, на самом деле мы подключаемся к базе данных MariaDB, о которой говорилось в предыдущей главе. PHP не видит никакой разницы между MySQL и MariaDB, поскольку они взаимозаменяемы. Я буду ссылаться на базу данных как на MySQL, потому что все используемые команды могут использоваться для подключения к серверу базы данных MySQL или MariaDB.
Первоначальная база данных MySQL предоставляла стандартизированный метод связи с сервером для таких клиентов, как MySQL Workbench и PHP. MariaDB скопировала этот стандарт, и все команды в PHP используют имя MySQL, поэтому для простоты я буду использовать термин MySQL в этой главе для обозначения базы данных.
Существует три способа подключения к серверу MySQL из PHP:
библиотека MySQL
библиотека MySQLi
библиотека PDO
Все они, по сути, выполняют одну и ту же работу — подключаются к базе данных и отправляют к ней запросы, — но используют для этого разный код.
Библиотека MySQL является старейшим методом подключения к базе данных и была представлена в PHP 2.0. Содержащиеся в нем функции минимальны, и он был заменен MySQLi с версии PHP 5.0 (выпущенной в 2004 году).
Для подключения и запроса базы данных с помощью старой библиотеки MySQL используются такие функции, как mysql_connect () и mysql_query (). Эти функции объявлены устаревшими, то есть их следует избегать, начиная с PHP 5.5, и полностью удалены из PHP, начиная с PHP 7.0.
Хотя большинство разработчиков увидели причину изменения сразу после выпуска PHP 5.0, в Интернете все еще есть сотни статей и примеров кода, использующих эти ныне несуществующие mysql_*функции, несмотря на то, что MySQLi фактически была предпочтительной библиотекой на протяжении пятнадцати лет. годы.
Если вы встретите пример кода, содержащий строку mysql_connect (), проверьте дату статьи. Это, вероятно, из начала
В PHP 5.0 была выпущена библиотека MySQLi — сокращение от «MySQL Improved» — для устранения некоторых ограничений исходной библиотеки MySQL. Вы можете легко определить использование MySQLi, потому что код будет использовать такие функции, как mysqli_connect () и mysqli_query ().
Вскоре после выпуска библиотеки MySQLi в PHP 5.0 был выпущен PHP 5.1 со значительным количеством изменений, которые помогли сформировать то, как мы пишем PHP сегодня (в основном это относится к
Между PDO и MySQLi есть несколько различий, но главное из них заключается в том, что вы можете использовать библиотеку PDO для подключения практически к любому серверу базы данных, например к серверу Oracle или Microsoft SQL Server. Для разработчиков самым большим преимуществом такого универсального подхода является то, что после того, как вы научитесь использовать библиотеку для взаимодействия с базой данных MySQL, вам будет очень просто взаимодействовать с другим сервером базы данных.
Возможно, код для PDO писать проще, и есть некоторые нюансы, которые могут сделать код PDO более читабельным — главное преимущество — именованные параметры в подготовленных операторах. (Не волнуйтесь, позже я объясню, что это значит.)
По этим причинам в большинстве последних
После этого небольшого урока истории вы, вероятно, захотите вернуться к написанию кода. Вот как вы используете PDO для установки соединения с сервером MySQL:
new PDO ('mysql: host=hostname; dbname=database’, 'username’,
'password’)
А пока представьте new PDOсебе встроенную функцию, как randфункцию, которую мы использовали в главе 2. Если вы думаете: «Эй, в именах функций не может быть пробелов!», вы умнее среднего медведя., и я объясню, что здесь происходит через мгновение. В любом случае он принимает три аргумента:
строка, указывающая тип базы данных (mysql:), имя хоста сервера (host=hostname;) и имя базы данных (dbname=database)
имя пользователя MySQL, которое вы хотите использовать в PHP
пароль MySQL для этого имени пользователя
Возможно, вы помните из главы 2, что
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb’, 'ijdbuser’,
'mypassword’) ;
Вероятно, вы видите, что происходит с двумя последними аргументами: это имя пользователя и пароль, которые вы создали ранее в этой главе.
Первый аргумент немного сложнее. Эта dbname=ijdbчасть указывает PDO использовать базу данных (также называемую схемой) с именем ijdb. Любой запрос, запущенный из PHP, по умолчанию будет использовать таблицы в этой схеме. SELECT * FROM jokeвыберет записи из jokeтаблицы в ijdbсхеме.
Даже если вы уже знакомы с PHP, PDO и MySQL, эта host=mysqlчасть выглядит запутанной. Обычно это будет host=localhost (ссылаясь на локальный компьютер, тот же компьютер, на котором работает PHP) или указание на определенное доменное имя, на котором размещена база данных, например host=sitepoint.com.
Почему это host=mysqlи что здесь имеется mysqlв виду? В Docker каждой службе дается имя. Если вы изучите
Аргументы в сторону, здесь важно видеть, что значение, возвращаемое, new PDOхранится в переменной с именем $pdo.
Сервер MySQL — это полностью отдельная часть программного обеспечения от
Примечание: по крайней мере, по умолчанию PHP можно настроить так, чтобы не возникало никаких исключений, и он просто не подключался. Как правило, это нежелательное поведение, так как гораздо сложнее понять, что пошло не так.
Если вам интересно, что значит «генерировать исключение PHP», приготовьтесь! Вы собираетесь открыть для себя еще несколько возможностей языка PHP.
Исключение PHP — это то, что происходит, когда вы указываете PHP выполнить задачу, а он не может ее выполнить. PHP попытается сделать то, что ему сказано, но потерпит неудачу; и чтобы сообщить вам о сбое, он выдаст вам исключение. Исключением является не более чем сбой PHP с определенным сообщением об ошибке. Когда возникает исключение, PHP останавливается. Никакие строки кода после ошибки выполняться не будут.
Ваша задача как ответственного разработчика — поймать это исключение и
Примечание: если вы не поймаете исключение, PHP прекратит выполнение вашего
Чтобы перехватить исключение, вы должны окружить код, который может вызвать исключение, try... catchоператором:
try {
⋮ do something risky
}
catch (ExceptionType $e) {
⋮ handle the exception
}
Вы можете думать об try... catchоператоре как об if... elseоператоре, за исключением того, что второй блок кода — это то, что происходит, если первый блок кода не выполняется.
Еще не запутались? Я знаю, что подбрасываю (без каламбура) много новых концепций, но будет больше смысла, если я соберу все это вместе и покажу вам, что у нас есть:
try {
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb’, 'ijdbuser’,
’mypassword’) ;
$output = 'Database connection established.';
}
catch (PDOException $e) {
$output = 'Unable to connect to the database server.';
}
include __DIR__. '/... /templates/output.html.php’;
Как видите, этот код является try... catchоператором. В tryблоке вверху мы пытаемся подключиться к базе данных с помощью new PDO. Если это удается, мы сохраняем полученный объект PDO $pdo, чтобы мы могли работать с нашим новым подключением к базе данных. Если соединение установлено успешно, $outputпеременной присваивается сообщение, которое будет отображаться позже.
Важно отметить, что внутри try... catchинструкции любой код после создания исключения не будет выполняться. В этом случае, если при подключении к базе данных возникает исключение (возможно, неверный пароль или сервер не отвечает), $outputпеременной никогда не будет присвоено значение «Соединение с базой данных установлено».
Если наша попытка подключения к базе данных не удалась, PHP выдаст PDOExceptionисключение, которое является типом исключения new PDO. Таким образом, наш catchблок говорит, что он перехватит исключение PDOException (и сохранит его в переменной с именем $e). Внутри этого блока мы устанавливаем переменную $output, чтобы она содержала сообщение о том, что пошло не так.
Однако это сообщение об ошибке не особенно полезно. Все, что он говорит нам, это то, что PDO не может подключиться к серверу базы данных. Было бы лучше иметь некоторую информацию о том, почему это произошло — например, потому что имя пользователя и пароль были недействительными.
Переменная $eсодержит сведения о возникшем исключении, включая сообщение об ошибке с описанием проблемы. Мы можем добавить это к выходной переменной, используя конкатенацию:
try {
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb’, 'ijdbuser’,
'mypassword’) ;
$output = 'Database connection established.';
}
catch (PDOException $e) {
$output = 'Unable to connect to the database server: '. $e→getMessage () ;
}
include __DIR__. '/... /templates/output.html.php’;
Примечание: $eпеременная не строка, а объект. Вскоре мы придем к тому, что это значит. Однако на данный момент все, что вам нужно знать, это то, что код $e→getMessage () получает сообщение об ошибке в зависимости от возникшего конкретного исключения.
Как и if... elseоператор, одна из двух ветвей try... catchоператора гарантированно будет выполнена. Либо код в tryблоке будет выполнен успешно, либо код в catchблоке будет выполнен. Независимо от того, было ли соединение с базой данных успешным, в переменной будет сообщение $output— либо сообщение об ошибке, либо сообщение о том, что соединение было успешным.
Наконец, независимо от того, tryбыл ли блок успешным или catchблок выполняется, шаблон output.html.phpвключается. Это общий шаблон, который просто отображает некоторый текст на странице:
<! doctype html>
<? php echo $output;? >
Полный код можно найти в примере:
Когда шаблон включен, он будет отображать либо сообщение об ошибке, либо сообщение «Соединение с базой данных установлено».
Я надеюсь, что вышеупомянутый код теперь имеет для вас
Примечание. Весь загруженный пример кода включает в себя схему с именем ijdb_sampleи пользователя с именем ijdb_sample, так что вы можете запустить его независимо от того, как вы назвали свою схему и пользователя. Файл, содержащий базу данных, предоставляется в формате database.sql, который вы можете импортировать.
Если вы используете предоставленное
Если вы хотите загрузить образцы данных в свою схему с помощью MySQL Workbench, импортируйте database.sqlих из projectкаталога, выбрав Data Import/Restore. Затем выберите Импорт из автономного файла, перейдите к database.sqlи выберите имя схемы в целевой схеме по умолчанию. Если вы создали
Ускоренный курс
Возможно, вы заметили, что слово «объект» начало проникать в мой словарный запас в предыдущем разделе. PDO является расширением PHP Data Objects и new PDOвозвращает объект PDO. В этом разделе я хотел бы объяснить, что такое объекты.
Возможно, вы сталкивались с термином
До сих пор мы писали наш
При этом расширение PDO, которое мы будем использовать для подключения к базе данных MySQL и работы с ней, спроектировано в стиле
Создание объекта очень похоже на вызов функции. На самом деле, вы уже видели, как это сделать:
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb’, 'ijdbuser’,
'mypassword’) ;
Ключевое newслово сообщает PHP, что вы хотите создать новый объект. Затем вы оставляете пробел и указываете имя класса, которое сообщает PHP, какой тип объекта вы хотите создать. Класс — это набор инструкций, которым PHP будет следовать для создания объекта. Вы можете думать о классе как о рецепте, например, о торте, а об объекте как о самом торте, приготовленном по рецепту. Разные классы могут производить разные предметы, так же как разные рецепты могут производить разные блюда.
Точно так же, как PHP поставляется с набором встроенных функций, которые вы можете вызывать, PHP поставляется с библиотекой классов, из которых вы можете создавать объекты. new PDO, следовательно, говорит PHP создать новый PDOобъект, то есть новый объект встроенного PDOкласса.
В PHP объект — это значение, такое же, как строка, число или массив. Вы можете сохранить объект в переменной или передать его функции в качестве аргумента — все то же самое, что вы можете делать с другими значениями PHP. Однако у объектов есть несколько полезных дополнительных функций.
$myObject = new SomeClass () ; // create an object
$myObject→someProperty = 123; // set a property’s value
echo $myObject→someProperty; // get a property’s value
В то время как массивы обычно используются для хранения списка похожих значений (например, массива дней рождения), объекты используются для хранения списка связанных значений (например, свойств соединения с базой данных). Тем не менее, если бы это были все объекты, в них не было бы особого смысла: мы могли бы с тем же успехом использовать массив для хранения этих значений, верно? Конечно, объекты делают больше.
Помимо хранения набора свойств и их значений, объекты могут содержать группу функций, предназначенных для предоставления нам более полезных функций. Функция, хранящаяся в объекте, называется методом (одно из самых запутанных названий в мире программирования, если вы спросите меня). Метод — это просто функция внутри класса. Еще больше сбивает с толку то, что когда мы приступаем к написанию собственных классов, методы определяются с помощью functionключевого слова! Даже опытные разработчики часто ошибочно используют функцию и метод как синонимы.
Чтобы вызвать метод, мы снова используем стрелочную нотацию — $myObject→someMethod ():
$myObject = new SomeClass () ; // create an object
$myObject→someMethod () ; // call a method
Как и отдельные функции, методы могут принимать аргументы и возвращать значения.
На данном этапе все это, вероятно, звучит немного сложно и бессмысленно, но поверьте мне: объединение наборов переменных (свойств) и функций (методов) в небольшие пакеты, называемые объектами, определенно приводит к гораздо более аккуратному и легко читаемому коду. задачи — работа с базой данных — лишь одна из них. Однажды вы, возможно, даже захотите разработать собственные классы, которые сможете использовать для создания объектов собственного изобретения.
Однако сейчас мы будем придерживаться классов, которые входят в состав PHP. Давайте продолжим работать с PDOсозданным нами объектом и посмотрим, что мы можем сделать, вызвав один из его методов.
Настройка подключения
До сих пор я показал вам, как создать PDOобъект для установления соединения с вашей базой данных MySQL и как отображать осмысленное сообщение об ошибке, когда
<? php
try {
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb’, 'ijdbuser’, 'mypassword’) ;
$output = 'Database connection established.';
} catch (PDOException $e) {
$output = 'Unable to connect to the database server: '. $e→getMessage () ;
}
include __DIR__. '/... /templates/output.html.php’;
Если предположить, что соединение установлено успешно, вам необходимо настроить его перед использованием. Вы можете настроить свое соединение, вызвав некоторые методы вашего нового PDOобъекта.
Перед отправкой запросов в базу данных нам нужно настроить кодировку символов нашего соединения с базой данных. Как я кратко упоминал в главе 2, вы должны использовать текст в кодировке
Даже если вы на 100% уверены, что ваш сайт будут использовать только англоязычные пользователи, существуют и другие проблемы, связанные с неустановленным набором символов. Если ваша
Поэтому теперь нам нужно настроить наш новый PDOобъект на использование кодировки
Мы можем указать PHP использовать
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’,
'mypassword’) ;
Примечание: если вы начнете искать, вы найдете разные способы установки кодировки, и в более ранних изданиях этой книги вам предлагалось использовать этот код:
$pdo→exec ('SET NAMES «utf8»') ;
Это связано с тем, что до версии PHP 5.3.6 параметр charset применялся PHP неправильно. Поскольку это исправлено в любой версии PHP, которую вы фактически собираетесь использовать, установка кодировки как части строки подключения является предпочтительным вариантом.
Таким образом, полный код, который мы используем для подключения к MySQL и последующей настройки этого подключения, показан ниже.
Пример:
<? php
try {
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’, 'mypassword’) ;
$output = 'Database connection established.';
} catch (PDOException $e) {
$output = 'Unable to connect to the database server: '. $e→getMessage () ;
}
include __DIR__. '/... /templates/output.html.php’;
Запустите этот пример в своем браузере. (Если вы поместили свой код базы данных index.phpвнутри publicкаталога и output.html.phpфайл в templatesкаталоге,
Если ваш сервер запущен и работает, и все работает правильно, вы должны увидеть сообщение об успешном завершении.
Успешное подключение
Если PHP не может подключиться к вашему серверу MySQL или если введенные вами имя пользователя и пароль неверны, вместо этого вы увидите экран, аналогичный показанному ниже. Чтобы убедиться, что ваш код обработки ошибок работает правильно, вы можете намеренно написать пароль с ошибкой, чтобы проверить его.
Ошибка подключения
Благодаря нашему catchблоку на страницу попало сообщение об ошибке из базы:
catch (PDOException $e) {
$output = 'Unable to connect to the database server: '. $e→getMessage () ;
}
Метод getMessage () возвращает сообщение с описанием возникшей исключительной ситуации. Есть и другие методы, в том числе getFile () и getLine (), для возврата имени файла и номера строки, для которой было выбрано исключение. Вы можете создать очень подробное сообщение об ошибке, подобное этому:
catch (PDOException $e) {
$output = 'Unable to connect to the database server: '. $e→getMessage (). ' in '.
$e→getFile (). ':'. $e→getLine () ;
}
Это невероятно полезно, если у вас большой
Если вам интересно, попробуйте вставить некоторые другие ошибки в код подключения к базе данных (например, неправильно написанное имя базы данных) и посмотрите на подробные сообщения об ошибках, которые появятся в результате. Когда вы закончите, и ваше соединение с базой данных работает правильно, вернитесь к простому сообщению об ошибке. Таким образом, ваши посетители не будут засыпаны технической тарабарщиной, если возникнет реальная проблема с вашим сервером базы данных.
Установив соединение и выбрав базу данных, вы готовы начать использовать данные, хранящиеся в базе данных.
Вам может быть интересно, что происходит с соединением с сервером MySQL после завершения выполнения скрипта. Если вы действительно этого хотите, вы можете заставить PHP отключиться от сервера, отбросив PDOобъект, представляющий ваше соединение. Вы делаете это, устанавливая переменную, содержащую объект, в null:
$pdo = null; // disconnect from the database server
Тем не менее, PHP автоматически закроет все открытые соединения с базой данных, когда завершит выполнение вашего скрипта, так что обычно вы можете просто позволить PHP очиститься за вами.
Отправка запросов SQL с помощью PHP
В главе 3 мы подключились к серверу базы данных MySQL с помощью MySQL Workbench, что позволило нам вводить
$pdo→exec ($query)
Вот $queryстрока, содержащая любой
Как вы знаете, если есть проблема с выполнением запроса (например, если вы допустили опечатку в своем
Рассмотрим следующий пример, в котором делается попытка создать таблицу шуток, которую мы создали в главе 3.
Пример:
try {
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’, 'mypassword’) ;
$sql = 'CREATE TABLE joke (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
joketext TEXT,
jokedate DATE NOT NULL
) DEFAULT CHARACTER SET utf8 ENGINE=InnoDB’;
$pdo→exec ($sql) ;
$output = 'Joke table successfully created.';
}
catch (PDOException $e) {
$output = 'Database error: '. $e→getMessage (). ' in '.
$e→getFile (). ':'. $e→getLine () ;
}
include __DIR__. '/... /templates/output.html.php’;
Обратите внимание, что мы используем ту же try... catchтехнику операторов для обработки возможных ошибок, вызванных запросом. Можно было бы использовать несколько try... catchблоков для отображения разных сообщений об ошибках — один для соединения и один для запроса, — но это может привести к значительному объему дополнительного кода.
Вместо этого я решил использовать один и тот же tryоператор для соединения и запроса. Блок try... catchпрекратит выполнение кода, как только произойдет ошибка, поэтому, если ошибка произойдет во время подключения к базе данных, $pdo→exec ($run) строка никогда не запустится, гарантируя, что при отправке запроса в базу данных соединение должно быть установлено.
Этот подход дает нам немного меньший контроль над отображаемым сообщением об ошибке, но избавляет от ввода try... catchоператора для каждой операции с базой данных. Позже в этой книге мы разобьем их на разные блоки, а пока держите все операции с базой данных в одном tryблоке.
Этот пример также использует getMessageметод для получения подробного сообщения об ошибке с сервера MySQL. На следующем изображении показана ошибка, которая отображается, например, когда таблица шуток уже существует.
Запрос CREATE TABLE не выполняется, поскольку таблица уже существует.
Для запросов DELETE, INSERT, и UPDATE (которые служат для изменения сохраненных данных) execметод возвращает количество строк таблицы (записей), затронутых запросом. Рассмотрим следующую команду SQL, которую мы использовали в главе 3 для установки дат всех шуток, содержащих слово «программист».
Пример:
try {
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’, 'mypassword’) ;
$sql = 'UPDATE joke SET jokedate="2021-04-01"
WHERE joketext LIKE «%programmer%»';
$affectedRows = $pdo→exec ($sql) ;
$output = 'Updated '. $affectedRows.' rows.';
}
catch (PDOException $e) {
$output = 'Database error: '. $e→getMessage (). ' in '.
$e→getFile (). ':'. $e→getLine () ;
}
include __DIR__. '/... /templates/output.html.php’;
Сохранив значение, возвращаемое методом exec, в $affectedRows, мы можем использовать переменную в $outputпеременной для печати в шаблоне.
На изображении ниже показан результат этого примера, предполагая, что в вашей базе данных есть только одна шутка «программист».
Отображается количество обновленных записей базы данных
Если вы обновите страницу, чтобы снова выполнить тот же запрос, вы должны увидеть изменение сообщения, как показано на следующем рисунке. Это указывает на то, что ни одна строка не была обновлена, поскольку новая дата, применяемая к шуткам, совпадает с существующей датой.
MySQL дает вам знать, когда вы тратите свое время впустую
SELECTзапросы обрабатываются немного
Обработка SELECTнаборов результатов
Для большинства
Метод запроса выглядит точно так же exec, как и в том смысле, что он принимает запрос SQL в качестве аргумента для отправки на сервер базы данных. Однако он возвращает PDOStatementобъект, представляющий набор результатов, содержащий список всех строк (записей), возвращенных из запроса:
<? php
try {
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’, 'mypassword’) ;
$sql = 'SELECT `joketext` FROM `joke`';
$result = $pdo→query ($sql) ;
} catch (PDOException $e) {
$error = 'Unable to connect to the database server: '. $e→getMessage (). ' in '.
$e→getFile (). ':'. $e→getLine () ;
}
При условии, что при обработке запроса не возникло ошибок, этот код сохранит результирующий набор (в виде PDOStatementобъекта) в переменную $result. Этот результирующий набор содержит текст всех шуток, хранящихся в jokeтаблице. Поскольку практического ограничения на количество шуток в базе данных нет, результирующий набор может быть довольно большим.
Я упоминал еще в главе 2, что whileцикл является полезной управляющей структурой, когда нам нужно выполнить цикл, но неизвестно, сколько раз. Мы не можем использовать forцикл, потому что не знаем, сколько записей вернул запрос. Действительно, здесь можно использовать whileцикл для обработки строк результирующего набора по одной за раз:
while ($row = $result→fetch ()) {
⋮ process the row
}
Условия для whileцикла, вероятно, отличаются от условий, к которым вы привыкли, поэтому позвольте мне объяснить, как это работает. Рассмотрим условие как утверждение само по себе:
$row = $result→fetch () ;
Метод fetchобъекта PDOStatementвозвращает следующую строку результирующего набора в виде массива (мы обсуждали массивы в главе 2). Когда в результирующем наборе больше нет строк, вместо этого fetchвозвращается. false (Это один из случаев, когда если попросить объект PDO сделать
Теперь приведенный выше оператор присваивает значение $rowпеременной, но в то же время оператор в целом принимает то же самое значение. Это то, что позволяет вам использовать оператор в качестве условия в whileцикле. Поскольку whileцикл будет повторяться до тех пор, пока его условие не будет оценено как false, этот цикл будет повторяться столько раз, сколько строк в результирующем наборе, $rowпринимая значение следующей строки каждый раз, когда цикл выполняется. Осталось выяснить, как извлекать значения из $rowпеременной при каждом выполнении цикла.
Строки результирующего набора, возвращаемого функцией fetch, представлены в виде ассоциативных массивов с индексами, названными в честь столбцов таблицы в результирующем наборе. Если $rowэто строка в нашем наборе результатов, $row['joketext’]это значение в joketextстолбце этой строки.
Наша цель в этом коде — сохранить текст всех шуток, чтобы мы могли отобразить их в шаблоне PHP. Лучший способ сделать это — сохранить каждую шутку как новый элемент в массиве $jokes:
while ($row = $result→fetch ()) {
$jokes[] = $row['joketext’];
}
Теперь, когда шутки извлечены из базы данных, мы можем передать их в шаблон PHP jokes.html.php.
Подводя итог, вот код контроллера для этого примера:
<? php
try {
$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’, 'mypassword’) ;
$sql = 'SELECT `joketext` FROM `joke`';
$result = $pdo→query ($sql) ;
while ($row = $result→fetch ()) {
$jokes[] = $row['joketext’];
}
} catch (PDOException $e) {
$error = 'Unable to connect to the database server: '. $e→getMessage (). ' in '.
$e→getFile (). ':'. $e→getLine () ;
}
include __DIR__. '/... /templates/jokes.html.php’;
Переменная $jokesпредставляет собой массив, в котором хранится список шуток. Если бы вы записали содержимое массива на PHP, это выглядело бы примерно так:
$jokes = [];
$jokes[0] = 'A programmer was found dead in the shower. The instructions read: lather, rinse, repeat.';
$jokes[1] = '! false — it\'s funny because it\'s true’;
$jokes[2] = 'A programmer\'s wife tells him to go to the store and «get a gallon of milk, and if they have eggs, get a dozen.» He returns with 13 gallons of milk.';
Однако данные извлекаются из базы данных, а не вводятся вручную в коде.
Вы заметили, что устанавливаются две разные переменные — $jokesи $error— в зависимости от того, успешно ли tryвыполнен блок.
В jokes.html.phpшаблоне нам нужно отобразить содержимое $jokesмассива или сообщение об ошибке, содержащееся в $errorпеременной.
Чтобы проверить, присвоено ли переменной значение, мы можем использовать issetфункцию, которую мы использовали ранее для проверки отправки формы. Шаблон может включать ifоператор, определяющий, отображать ли ошибку или список шуток:
if (isset ($error)) {
? >
<? php
echo $error;
? >
}
else {
: display the jokes
}
Здесь нет ничего нового, но для отображения приколов нам нужно отобразить содержимое $jokesмассива. В отличие от других переменных, которые мы использовали до сих пор, $jokesмассив содержит больше, чем просто одно значение.
Наиболее распространенный способ обработки массива в PHP — использование цикла. Мы уже видели whileпетли и forпетли. Цикл foreachособенно полезен для обработки массивов:
foreach (array as $item) {
⋮ process each $item
}
Вместо условия круглые скобки в начале foreachцикла содержат массив, за которым следует ключевое слово as, а затем имя новой переменной, которая будет использоваться для хранения каждого элемента массива по очереди. Затем тело цикла выполняется один раз для каждого элемента массива. Каждый раз этот элемент сохраняется в указанной переменной, чтобы код мог получить к нему прямой доступ.
Обычно в foreachшаблонах PHP используется цикл для отображения каждого элемента массива по очереди. Вот как это может выглядеть для нашего $jokesмассива:
<? php
foreach ($jokes as $joke) {
? >
⋮ HTML code to output each $joke
<? php
}
? >
При таком сочетании
foreach (array as $item):
⋮ process each $item
endforeach;
Два фрагмента кода функционально идентичны, но последний выглядит более дружелюбно в сочетании с
<? php foreach ($jokes as $joke):? >
⋮ HTML code to output each $joke
<? php endforeach;? >
То же самое можно сделать с ifоператором, чтобы было удобнее смотреть внутри шаблонов HTML, избегая фигурных скобок:
<? php if (isset ($error)):? >
<? php echo $error;? >
<? php else:? >
: display the jokes
<? php endif;? >
Имея в руках эти новые инструменты, мы можем написать наш шаблон для отображения списка шуток.
Пример:
<! doctype html>
<? php if (isset ($error)):? >
<? php echo $error;? >
<? php else:? >
<? php foreach ($jokes as $joke):? >
<? php echo htmlspecialchars ($joke, ENT_QUOTES, '
UTF-8 ')? >
<? php endforeach;? >
<? php endif;? >
Либо$error текст отображается на странице, либо каждая шутка отображается в абзаце (), заключенном в
), поскольку мы фактически цитируем автора каждой шутки на этой странице.Поскольку шутки могут содержать символы, которые могут быть интерпретированы как
HTML-код (например, <, >, или &), мы должны htmlspecialcharsубедиться, что они переведены в объекты символов HTML (то есть, <, > и &), чтобы они отображались правильно.На следующем изображении показано, как будет выглядеть эта страница после того, как вы добавите пару шуток в базу данных.
Список шуток из базы данных
Помните, как мы использовали whileцикл в нашем контроллере для выборки строк из PDOStatementнабора результатов по одной?
while ($row = $result→fetch ()) {
$jokes[] = $row['joketext’];
}
Оказывается, PDOStatementобъекты спроектированы так, чтобы вести себя точно так же, как массивы, когда вы передаете их в foreachцикл. Поэтому вы можете немного упростить код обработки базы данных, используя foreachцикл вместо whileцикла:
foreach ($result as $row) {
$jokes[] = $row['joketext’];
}
Я буду использовать этот аккуратныйforeach форму в остальной части этой книги.
Еще один полезный инструмент, который предлагает PHP, — это сокращенный способ вызова echoкоманды, который, как вы уже видели, нам приходится часто использовать. Наши echoзаявления выглядят так:
<? php echo $variable;? >
Вместо этого мы можем использовать это:
<?=$variable? >
Это делает то же самое. <?=означает echoи дает вам немного более короткий способ печати переменных. Однако на это есть ограничение: если вы используете<?=, вы можете только печатать. Вы не можете включать ifоператоры, forоператоры и т. д., хотя вы можете использовать конкатенацию, и за ней может следовать вызов функции.
Вот обновленный шаблон, использующий сокращенное эхо.
Пример:
MySQL-ListJokes-Shorthand <! doctype html>
<? php if (isset ($error)):? >
<?=$error? >
<? php else:? >
<? php foreach ($jokes as $joke):? >
<?=htmlspecialchars ($joke, ENT_QUOTES, '
UTF-8 ')? >
<? php endforeach;? >
<? php endif;? >
С этого момента я буду использовать сокращенную запись, если она применима.
Примечание: в версиях PHP до 5.4 эта сокращенная запись требовала включения довольно необычной настройки PHP, поэтому ее использование не рекомендуется из соображений совместимости. Использование сокращенной записи могло привести к тому, что ваш код перестал работать при перемещении с сервера, на котором он был включен, на другой, на котором он не работал.
Начиная с PHP 5.4 (т. е. в любой версии, с которой вы реально столкнетесь в наши дни), сокращенное эхо работает независимо от настроек PHP, поэтому вы можете безопасно использовать его, не беспокоясь о том, что оно может работать не на всех серверах.
Думая о будущем
В примере, который мы только что рассмотрели, мы создали шаблон, jokes.html.phpкоторый содержит весь
Я немного забегаю вперед, но всегда стоит подумать о том, как будет расти проект. Если мы применим подход, который мы только что использовали jokes.html.php, к остальным шаблонам — addjoke.html.php, home.html.php, contact.html.php, login.html.php т. д. — мы получим много повторяющегося кода.
Для каждой страницы
<! doctype html>
<? php if (isset ($error)):? >
<?=$error? >
<? php else:? >
: do whatever is required for this page: show text,
: show a form, list records from the database, etc.
<? php endif;? >
Для программиста повторение кода — одна из худших вещей, которые вы можете сделать. На самом деле программисты часто ссылаются на DRY., который означает «Не повторяйся». Если вы обнаружите, что повторяете участки кода, почти наверняка есть лучшее решение.
Все лучшие программисты ленивы, а повторение кода означает повторение работы. Использование этого подхода копирования/вставки для шаблонов делает
<! doctype html>
- Home
- Jokes List
<? php if (isset ($error)):? >
<?=$error? >
<? php else:? >
: do whatever is required for this page: show text,
: show a form, list jokes, etc.
<? php endif;? >
Мы столкнемся с проблемой в 2022 году! Если шаблоны для всех страниц
Мы могли бы быть умнее и динамически считывать дату с часов сервера (echo date ('Y’) ;если вам интересно!), чтобы избежать этой проблемы, но что, если бы мы захотели добавить
<? php include 'nav.html.php’;? >
<? php if (isset ($error)):? >
<?=$error? >
<? php else:? >
: do whatever is required for this page: show text,
: show a form, list jokes, etc.
<? php endif;? >
<? php include 'footer.html.php’;? >