_errors = []; $validator = new ModelValidator(); parent::__construct($validator); } /** * @return static */ public static function create() { $created = parent::create(); if (is_bool($created)) { throw new \Exception('ORM is not initialised'); } return $created; } /** * Creates a row, or updates it if already exists. It tries to find the existing * row by `id` (if given in `$data`), or by the given `$keys`. If `$onCreate` is * given, it's used to transform `$data` before creating the new row. * * @param array $data * @param array|bool $keys * @param callable|bool $onCreate * @return self */ static protected function _createOrUpdate($data = [], $keys = false, $onCreate = false) { $model = false; if (isset($data['id']) && (int)$data['id'] > 0) { $model = static::findOne((int)$data['id']); } if ($model === false && !empty($keys) && is_array($keys)) { foreach ($keys as $field => $value) { if ($model === false) { $model = static::where($field, $value); } else { $model = $model->where($field, $value); } } if ($model !== false) $model = $model->findOne(); } if ($model === false) { if (!empty($onCreate) && is_callable($onCreate)) { $data = $onCreate($data); } $model = static::create(); $model->hydrate($data); } else { unset($data['id']); $model->set($data); } return $model->save(); } static public function createOrUpdate($data = []) { return self::_createOrUpdate($data); } public function getErrors() { if (empty($this->_errors)) { return false; } else { return $this->_errors; } } public function setError($error = '', $errorCode = null) { if (!$errorCode) { $errorCode = count($this->_errors); } if (!empty($error)) { if (is_array($error)) { $this->_errors = array_merge($this->_errors, $error); $this->_errors = array_unique($this->_errors); } else { $this->_errors[$errorCode] = $error; } } } /** * @return static * @phpstan-ignore-next-line Our Model has incompatible return type with parent */ public function save() { $this->setTimestamp(); $this->newRecord = $this->isNew(); try { parent::save(); } catch (\MailPoetVendor\Sudzy\ValidationException $e) { $this->setError($e->getValidationErrors()); } catch (\PDOException $e) { switch ($e->getCode()) { case 23000: preg_match("/for key '(?:.*\.)*(.*?)'/i", $e->getMessage(), $matches); if (isset($matches[1])) { $column = $matches[1]; $this->setError( sprintf( // translators: %1$s is the name of the database column. __('Another record already exists. Please specify a different "%1$s".', 'mailpoet'), $column ), Model::DUPLICATE_RECORD ); } else { $this->setError($e->getMessage()); } break; default: $this->setError($e->getMessage()); } } return $this; } public function isNew() { return (isset($this->newRecord)) ? $this->newRecord : parent::isNew(); } public function trash() { return $this->set_expr('deleted_at', 'NOW()')->save(); } public static function bulkTrash($orm) { $model = get_called_class(); $count = self::bulkAction($orm, function($ids) use ($model) { $model::rawExecute(join(' ', [ 'UPDATE `' . $model::$_table . '`', 'SET `deleted_at` = NOW()', 'WHERE `id` IN (' . rtrim(str_repeat('?,', count($ids)), ',') . ')', ]), $ids); }); return ['count' => $count]; } public static function bulkDelete($orm) { $model = get_called_class(); $count = self::bulkAction($orm, function($ids) use ($model) { $model::whereIn('id', $ids)->deleteMany(); }); return ['count' => $count]; } public function restore() { return $this->set_expr('deleted_at', 'NULL')->save(); } public static function bulkRestore($orm) { $model = get_called_class(); $count = self::bulkAction($orm, function($ids) use ($model) { $model::rawExecute(join(' ', [ 'UPDATE `' . $model::$_table . '`', 'SET `deleted_at` = NULL', 'WHERE `id` IN (' . rtrim(str_repeat('?,', count($ids)), ',') . ')', ]), $ids); }); return ['count' => $count]; } public static function bulkAction($orm, $callback = false) { $total = $orm->count(); if ($total === 0) return false; $rows = $orm->select(static::$_table . '.id') ->offset(null) ->limit(null) ->findArray(); $ids = array_map(function($model) { return (int)$model['id']; }, $rows); if (is_callable($callback)) { $callback($ids); } // get number of affected rows return $orm->get_last_statement() ->rowCount(); } public function duplicate($data = []) { $model = get_called_class(); $modelData = array_merge($this->asArray(), $data); unset($modelData['id']); $duplicate = $model::create(); $duplicate->hydrate($modelData); $duplicate->set_expr('created_at', 'NOW()'); $duplicate->set_expr('updated_at', 'NOW()'); if (isset($modelData['deleted_at'])) { $duplicate->set_expr('deleted_at', 'NULL'); } $duplicate->save(); return $duplicate; } public function setTimestamp() { if ($this->createdAt === null) { $this->set_expr('created_at', 'NOW()'); } } public static function getPublished() { return static::whereNull('deleted_at'); } public static function getTrashed() { return static::whereNotNull('deleted_at'); } /** * Rethrow PDOExceptions to prevent exposing sensitive data in stack traces */ public static function __callStatic($method, $parameters) { try { return parent::__callStatic($method, $parameters); } catch (\PDOException $e) { throw new \Exception($e->getMessage()); } } public function validate() { $success = true; foreach (array_keys($this->_validations) as $field) { $success = $success && $this->validateField($field, $this->$field); } $this->setError($this->getValidationErrors()); return $success; } public function __get($name) { $value = parent::__get($name); if ($value !== null) { return $value; } $name = Helpers::camelCaseToUnderscore($name); return parent::__get($name); } public function __set($name, $value) { $name = Helpers::camelCaseToUnderscore($name); parent::__set($name, $value); } public function __isset($name) { $name = Helpers::camelCaseToUnderscore($name); return parent::__isset($name); } }