Я изучаю php oop, и я схожу с ума от этого …
Я хочу обновить свою базу данных с помощью формы на своем веб-сайте.
Я создаю свою модель следующим образом:
public function update(Post $post){ $q = $this->_db->prepare('UPDATE posts SET title = :title, featuredImg = :featuredImg, content = :content, author = :author, date = :date, header = :header WHERE id = :id'); $q->bindValue(':title', $post->title(), PDO::PARAM_STR ); $q->bindValue(':content', $post->content(), PDO::PARAM_STR ); $q->bindValue(':author', $post->author(), PDO::PARAM_STR ); $q->bindValue(':header', $post->header(), PDO::PARAM_STR ); $q->bindValue(':date', $post->date(), PDO::PARAM_STR ); $q->bindValue(':featuredImg', $post->featuredImg(), PDO::PARAM_STR); $q->bindValue(':id', $post->id(), PDO::PARAM_INT); $q->execute();
и мой метод:
public function updatePost(){ $manager = new PostsManager($this->db); if($_SERVER['REQUEST_METHOD'] == 'POST'){ $p = new Post([ 'title' => $_POST['title'], 'header' => $_POST['header'], 'author' => $_POST['author'], 'date' => date("Ymd H:i:s"), 'content' => $_POST['content'] ]); $manager->update($p); } }
Когда я var_dump($p)
меня есть мои данные из моей формы, но когда я пытаюсь использовать его с помощью метода $manager->update($p)
он говорит null, поэтому ничего не меняется в моем db.
Я не использую Doctrine или другой уровень DBAL.
Пожалуйста, скажите мне, что я делаю плохо? Повторяю, я учусь. большое спасибо
РЕДАКТИРОВАТЬ :
Мой плохой, когда я var_dump($p)
, мой id равен null
, это не хорошо? :
$object(Post)[13] private '_id' => null private '_title' => string 'Spicy jalapeno enim flank laborum prosciutto' (length=44) private '_content' => string 'Commodo shankle t-bone, pork loin occaecat ea andouille shoulder venison sausage chicken ... (length=1573) private '_author' => string 'aaaaaaaaaaa' (length=11) private '_date' => null private '_header' => string 'Spicy jalapeno ...' (length=190) private '_featuredImg' => null_
Просто добавив ID в мой объект Post, и все возвращается в порядок
$post = new Post([ 'id' => $_POST['id'], 'title' => $_POST['title'], 'header' => $_POST['header'], 'author' => $_POST['author'], 'date' => date("Ymd H:i:s"), 'content' => $_POST['content'], 'featuredImg' => $image ]);
Проблема, похоже, не связана с вышеприведенным кодом, поставив инструкцию prepare()
в блок try-catch , вы сможете узнать, не является ли она проблемой в вашем SQL-запросе или PHP-код ….
try { //... $q = $this->_db->prepare('UPDATE posts SET title = :title, featuredImg = :featuredImg, content = :content, author = :author, date = :date, header = :header WHERE id = :id'); //... $q->execute(); } catch (Exception $e) { echo "Problem happened: " . $e->getMessage() }
Таким образом, вы можете получить больше, чем NULL в качестве ответа.
Ваша функция добавления работает с одной и той же системой?
Мог бы попробовать это
public function updatePost() { $manager = new PostsManager($this->db); if($_SERVER['REQUEST_METHOD'] == 'POST'){ $p = new Post($_POST); $manager->update($p); } }
Или, может быть, ваш id пуст
В качестве теста запустите запрос на обновление без использования PHP. Дайте короткие тестовые значения в вашем заявлении sql.
@Matteo прав: ваш метод update () ничего не возвращает. Вот почему вызов var_dump ($ manager-> update ($ p)) отображает «null».
Убедитесь, что тип данных каждого свойства модели (который вы хотите сохранить) соответствует соответствующему типу данных столбцов в таблице базы данных. То же самое относится к значению каждого свойства модели. Убедитесь, что длина каждого из ваших значений модели не превышает соответствующую длину, определенную в таблице базы данных.
Если вы хотите сохранить значение NULL, вы должны убедиться, что соответствующий столбец таблицы принимает значение NULL как значение.
Свойство «id» model должно иметь значение, отличное от NULL.
Я бы заподозрил некоторые проблемы с свойством model date, по поводу соответствия типу данных в таблице базы данных. Кроме того, имя «дата» того же свойства позволяет мне думать о зарезервированном слове «дата» в MySQL. Я возвращаюсь к строке «$ q-> bindValue (': date', $ post-> date (), PDO :: PARAM_STR);".
@Lucas Barbosa дал очень хорошую рекомендацию применить блок (-ы) try-catch. Я хочу добавить следующее: ссылаясь на обработку исключений для методов подготовки () и execute () в методе update ():
Вы должны «отдельно» обрабатывать подготовку инструкции sql (PDO :: prepare ()) и выполнение (результирующего) подготовленного PDO-оператора (PDOStatement :: execute ()). В вашем случае я ссылаюсь здесь на «$ q = $ this -> _ db-> prepare (…)», соответственно, на часть «$ q-> execute ()».
Кроме того, PDO :: prepare () может вызывать PDOException ИЛИ значение FALSE (см. «Возвращаемые значения» в документации PDO :: подготовить). Поэтому вы должны обрабатывать оба случая.
Обратите внимание, что в моем коде используется «PDOException». Это подкласс «Исключение». Таким образом, блокировка catch для исключения PDOException должна предшествовать блокировке catch для «исключения».
После обнаружения и обработки исключения программа продолжает свое нормальное выполнение с помощью операторов, следующих за последним блоком catch. Сразу после обработки исключения вы можете остановить выполнение с помощью exit () или die () (я не уверен в die ()). Но это зависит от того, что вы хотите сделать с обработкой исключений. Кроме того, вы можете также использовать блок «finally» после «catch» -block (s), если вы считаете, что подходит для ваших проектов. См. «Исключение» в документации php.net.
Теперь, чтобы показать вам, что я имею в виду здесь, я буду повторно использовать ваш метод update () соответственно:
use Exception; use PDOException; public function update(Post $post){ // The try-catch block to handle PDO::prepare() exceptions. try { $q = $this->_db->prepare('<SQL-STATEMENT>'); // Here is handled the case of PDO::prepare() returning FALSE but not throwing an exception. if (!$q) { throw new Exception('The SQL statement can not be prepared!'); } } catch (PDOException $pdoException) { // Here is catched the PDOException (!) thrown by the PDO::prepare(). echo $pdoException->getMessage(); exit(); } catch (Exception $exception) { // Here is catched the Exception (!) thrown from inside of if-statement. echo $exception->getMessage(); exit(); } // [...] Binding values statements. // The try-catch block to handle PDO::prepare() exceptions. try { if (!$q->execute()) { throw new Exception('The PDO statement can not be executed!'); } } catch (Exception $exception) { echo $exception->getMessage(); exit(); } /* * Return TRUE if the number of affected records > 0 (update opreration successful), or FALSE otherwise. * This way you can later in the updatePost() check if the update operation was successfuly executed. */ return $q->rowCount() > 0 ? TRUE : FALSE; }
И вот вариант, в котором исключения переустанавливаются на верхние уровни для обработки (поэтому из класса менеджера сообщений в класс контроллера).
Итак, в классе контроллера:
use Exception; public function updatePost(){ if($_SERVER['REQUEST_METHOD'] == 'POST'){ // [...] // THIS try-catch BLOCK SHOULD BE MOVED TO THE APP ENTRY POINT (LIKE index.php, or in the bootstrap.php). try { $manager->update($p); } catch (Exception $exception) { // Catch exceptions from PDO::prepare() and PDOStatement::execute() in update() method. echo $exception->getMessage(); exit(); } } }
В классе менеджера сообщений:
use Exception; use PDOException; // User defined exception code to pass over when a PDOException is raised. const EXCEPTION_CODE_PDO = 37; public function update(Post $post){ // The try-catch block to handle PDO::prepare() AND PDOStatement::execute() exceptions. try { $q = $this->_db->prepare('<SQL-STATEMENT>'); // Here is handled the case of PDO::prepare() returning FALSE but not throwing an exception. if (!$q) { throw new Exception('The SQL statement can not be prepared!'); } // Execute the PDO statement. if (!$q->execute()) { throw new Exception('The PDO statement can not be executed!'); } } catch (PDOException $pdoException) { // Here is catched the PDOException (!) thrown by the PDO::prepare(). // WE PASS THE CATCHED $pdoException TO THE UPPER LEVEL (to be catched in updatePost()). throw new Exception($pdoException->getMessage(), self::EXCEPTION_CODE_PDO, $pdoException); exit(); } /* * Return TRUE if the number of affected records > 0 (update opreration successful), or FALSE otherwise. * This way you can later in the updatePost() check if the update operation was successfuly executed. */ return $q->rowCount() > 0 ? TRUE : FALSE; }
Надеюсь, это помогло.
EDIT (04.05.2017):
Мне нравится как-то дать еще несколько примеров обработки исключений …
Как я уже писал, вы можете перебросить исключения на верхние уровни. Уровень восходящего потока может быть точкой входа вашего приложения (index.php, bootstrap.php). Исключения в этом месте пойманы и обработаны.
Итак, давайте, просто для ясности, перекодируем ваш метод Controller :: updatePost () таким образом, что (внутри него) вместо $ manager-> update () заменяется двумя операциями, например $ manager-> prepareStatement ( ) и $ manager-> executeStatement () (игнорируя объект $post
):
class Controller { public function updatePost() { $manager = new PostsManager(/*...*/); // Instead of update() $pdoStatement = $manager->prepareStatement(); $manager->executeStatement($pdoStatement); } }
Теперь определите свои собственные классы исключений – давайте прикрепим их к «My». Обратите внимание на дерево наследования AND конструктор базового класса, который я добавил только для того, чтобы подчеркнуть параметр Throwable $previous
:
/** Use of PHP (proprietary) exception class. */ use Exception; /** Your own base exception class. */ class MyException extends Exception { public function __construct(string $message = "", int $code = 0, Throwable $previous = null) { parent::__construct($message, $code, $previous); } public function handle() { // Print the message on screen. $this->display($this->getMessage()); // Write the exception message in a log file. $this->log($this); } } class MyPdoException extends MyException { public function handle() { // Print on screen and log exception. parent::handle(); // Save exception in some database. $this->store($this->getCode(), $this->getLine(), $this->getMessage()); } } class MyNullSqlStatementException extends MyPdoException { public function handle() { // Print on screen, log and save exception in db. parent::handle(); // Send mail with exception details. $this->sendMessage($this->getCode(), $this->getLine(), $this->getMessage(), $this->getTraceAsString()); } }
А теперь давайте посмотрим на PostsManager::prepareStatement()
:
*) Вы создаете свой SQL-запрос с PDO :: prepare () в рамках try-catch. *) По некоторым причинам (например, оператор sql имеет значение NULL), PDOException возникает системой (PHP). *) Вы поймаете это прямо на месте. В этот момент вы бросаете свое собственное исключение MyNullSqlStatementException:
use MyNullSqlStatementException; use PDOException; class PostsManager { // User defined exception code. const EXCEPTION_CODE_PDO_NULL_STATEMENT = 37; public function prepareStatement() { try { $pdoStatement = $this->_db->prepare('<SQL-STATEMENT>'); } catch (PDOException $pdoException) { // Catch PDOException and throw YOUR OWN to upper levels. throw new MyNullSqlStatementException( 'Error: It seems that no statement has been provided!' , self::EXCEPTION_CODE_PDO_NULL_STATEMENT , $pdoException ); exit(); } return $pdoStatement; } }
Заметить, что:
$pdoException
) в качестве аргумента. Это представляет собой Throwable $previous
конструктор-параметр MyNullSqlStatementException
(подкласс MyException
). Таким образом, когда вы, наконец, обрабатываете настраиваемое исключение, у вас есть ссылка на исходный PDOException
PHP-объект PDOException
. Теперь вы закончили с PostsManager::prepareStatement()
. Итак, давайте посмотрим на PostsManager::executeStatement()
:
use MyPdoException; use PDOException; class PostsManager { // User defined exception code. const EXCEPTION_CODE_PDO_FAILED_EXECUTE_STATEMENT = 37; public function executeStatement(PDOStatement $pdoStatement) { if (!$pdoStatement->execute()) { // Throw your own exception to upper levels. throw new MyPdoException( 'Error: The given statement can not be executed!' , self::EXCEPTION_CODE_PDO_FAILED_EXECUTE_STATEMENT , NULL ); } } }
Заметить, что:
Throwable $previous
равно NULL (на самом деле даже не требуется передавать); Теперь вы также закончили с PostsManager::executeStatement()
. Итак, мы снова поднимаемся на один уровень до Controller::updatePost()
:
class Controller { public function updatePost() { $manager = new PostsManager(/* ... */); // Instead of update() $pdoStatement = $manager->prepareStatement(); $manager->executeStatement($pdoStatement); } }
Да, вы правы: никаких изменений здесь! … Ничего не нужно было делать, потому что обработка исключений имеет место на уровне повышения уровня приложения (например, index.php или bootstrap.php). Так что никаких попыток, например, обработки исключений для исключения MyNullSqlStatementException
созданного PostsManager::prepareStatement()
, или для MyPdoException
созданного PostsManager::executeStatement()
. Поэтому здесь вам не нужно заботиться об этом (больше, как я показал вам в примерах до этого EDIT).
НО вы можете также выбросить другой тип исключения, если вам нужно. Итак, скажем, теперь вы хотите также отобразить статус операции обновления для пользователя. Для этого вы используете Twig (механизм шаблонов), и вы пытаетесь отобразить шаблон, которому должен быть передан статус обновления. Но, по какой-то зависящей от Twig причине, операция рендеринга терпит неудачу. Затем вы должны поймать исключение и бросить обычай (скажем, MyTwigException) на верхние уровни.
Вот измененный Controller::updatePost()
:
class Controller { public function updatePost() { $manager = new PostsManager(/* ... */); // Instead of update() $pdoStatement = $manager->prepareStatement(); $updateStatus = $manager->executeStatement($pdoStatement); // Render the template and inject update status. $this->getView()->render( array('updateStatus' => $updateStatus) ); } }
Вот View::render()
:
use Twig_Error; use Twig_TemplateWrapper; class View { const EXCEPTION_CODE_TWIG = 2; /** * A twig template instance. * * @var Twig_TemplateWrapper */ private $twigTemplate; /** * Renders a template which becomes the given values injected. * * @param array $context * @return $this * @throws MyTwigException */ public function render(array $context = array()) { try { //... // Render the template. echo $this->twigTemplate->render($context); } catch (Twig_Error $twigException) { throw new MyTwigException( 'An error occured during template rendering!' , self::EXCEPTION_CODE_TWIG , $twigException ); } return $this; } }
Таким образом, если возникает исключение Twig-specific, мы MyTwigException
пользовательское MyTwigException
с сообщением, соответствующим кодом исключения и объектом $twigException
как Throwable $previous
(для ссылки ниже на том уровне, на котором обрабатываются исключения).
Итак, нам также нужен класс MyTwigException
:
use MyException; class MyTwigException extends MyException { public function handle() { // Print on screen and log exception. parent::handle(); // Print also a "TODO" to the user. $this->display('Please repair your template engine!'); } }
И все это касается «передачи» исключений на верхние уровни и с повторным переворотом исключений. Все эти брошенные / повторно исключенные исключения будут обрабатываться в index.php или bootstrap.php (точка входа в приложение) следующим образом:
// Custom exceptions use MyException; use MyPdoException; use MyTwigException; use MyNullSqlStatementException; // // PHP exceptions use Exception; use PDOException; try { //... // Create controller and call action. $dispatched = $dispatcher->dispatch(); } catch (MyNullSqlStatementException $exc) { $exc->handle(); } catch (MyPdoException $exc) { $exc->handle(); } catch (MyTwigException $exc) { $exc->handle(); } catch (PDOException $exc) { // PHP proprietary! $exc->handle(); } catch (MyException $exc) { $exc->handle(); } catch (Exception $exc) { // PHP proprietary! // Some function to display on screen. // Some function to log in a file. }
NOTA BENE: порядок, в котором расположены блокировки catch
, очень важен. Они лучше следуют порядку дерева наследования (от дочерних классов до их родителей), так что более «подробные», «специфические», «более специализированные» методы в дочерних классах используются для обработки перечеркнутых исключений. В нашем примере дерево наследования выглядит следующим образом:
MyNullSqlStatementException
является дочерним элементом MyPdoException
; MyPdoException
– дочерний MyException
; MyTwigException
является дочерним элементом MyException
; MyException
– дочерний элемент Exception
(PHP);
PDOException
является дочерним элементом RuntimeException
и реализует Throwable
;
RuntimeException
является дочерним элементом Exception
и реализует Throwable
; Exception
реализует Throwable
. MyException::handle()
обрабатывает общий материал – например, отображение и регистрацию исключения. Но MyPdoException::handle()
более специализирован: он даже сохраняет исключение в базе данных. И MyNullSqlStatementException::handle()
– наиболее специфический метод, потому что он обрабатывает как его родительский MyNullSqlStatementException::handle()
, но также отправляет электронное письмо с подробными сведениями об этом разработчику программного обеспечения.
Вот и все. Надеюсь, этот EDIT тоже помог.