PageRenderTime 28ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/mailpoet/lib/Models/Model.php

https://gitlab.com/remyvianne/krowkaramel
PHP | 404 lines | 215 code | 44 blank | 145 comment | 29 complexity | a827bf21dbcba092bf09ee6226bbdf10 MD5 | raw file
  1. <?php
  2. namespace MailPoet\Models;
  3. if (!defined('ABSPATH')) exit;
  4. use MailPoet\Util\Helpers;
  5. use MailPoet\WP\Functions as WPFunctions;
  6. /**
  7. * @method static array|string getConfig($key = null, $connection_name = self::DEFAULT_CONNECTION)
  8. * @method static null resetConfig()
  9. * @method static self forTable($table_name, $connection_name = self::DEFAULT_CONNECTION)
  10. * @method static null setDb($db, $connection_name = self::DEFAULT_CONNECTION)
  11. * @method static null resetDb()
  12. * @method static null setupLimitClauseStyle($connection_name)
  13. * @method static \PDO getDb($connection_name = self::DEFAULT_CONNECTION)
  14. * @method static bool rawExecute($query, $parameters = array())
  15. * @method static \PDOStatement getLastStatement()
  16. * @method static string getLastQuery($connection_name = null)
  17. * @method static array getQueryLog($connection_name = self::DEFAULT_CONNECTION)
  18. * @method array getConnectionNames()
  19. * @method $this useIdColumn($id_column)
  20. * @method $this|false findOne($id=null)
  21. * @method static static|false findOne($id=null)
  22. * @method array findMany()
  23. * @method static array findMany()
  24. * @method \MailPoetVendor\Idiorm\IdiormResultSet findResultSet()
  25. * @method array findArray()
  26. * @method static array findArray()
  27. * @method $this forceAllDirty()
  28. * @method $this select_expr(string $expr, string $alias=null)
  29. * @method $this rawQuery($query, $parameters = array())
  30. * @method static $this rawQuery($query, $parameters = array())
  31. * @method $this tableAlias($alias)
  32. * @method static $this tableAlias($alias)
  33. * @method int countNullIdColumns()
  34. * @method $this select($column, $alias=null)
  35. * @method static $this select($column, $alias=null)
  36. * @method $this selectExpr($expr, $alias=null)
  37. * @method static $this selectExpr($expr, $alias=null)
  38. * @method $this selectMany(...$values)
  39. * @method static static selectMany(...$values)
  40. * @method static selectManyExpr($values)
  41. * @method $this rawJoin(string $table, string|array $constraint, string $table_alias, array $parameters = array())
  42. * @method $this innerJoin(string $table, string|array $constraint, string $table_alias=null)
  43. * @method $this join(string $table, string|array $constraint, string $table_alias=null)
  44. * @method static static join(string $table, string|array $constraint, string $table_alias=null)
  45. * @method $this leftOuterJoin(string $table, string|array $constraint, string $table_alias=null)
  46. * @method $this rightOuterJoin(string $table, string|array $constraint, string $table_alias=null)
  47. * @method $this fullOuterJoin(string $table, string|array $constraint, string $table_alias=null)
  48. * @method $this where($column_name, $value=null)
  49. * @method static $this where($column_name, $value=null)
  50. * @method $this whereEqual($column_name, $value=null)
  51. * @method static $this whereEqual($column_name, $value=null)
  52. * @method $this whereNotEqual($column_name, $value=null)
  53. * @method static $this whereNotEqual($column_name, $value=null)
  54. * @method $this whereIdIs($id)
  55. * @method $this whereAnyIs($values, $operator='=')
  56. * @method static $this whereAnyIs($values, $operator='=')
  57. * @method $this whereIdIn($ids)
  58. * @method static static whereIdIn($ids)
  59. * @method $this whereLike($column_name, $value=null)
  60. * @method static $this whereLike($column_name, $value=null)
  61. * @method $this whereNotLike($column_name, $value=null)
  62. * @method $this whereGt($column_name, $value=null)
  63. * @method static $this whereGt($column_name, $value=null)
  64. * @method static $this whereLt($column_name, $value=null)
  65. * @method $this whereGte($column_name, $value=null)
  66. * @method $this whereLte($column_name, $value=null)
  67. * @method $this whereIn($column_name, $values)
  68. * @method static $this whereIn($column_name, $values)
  69. * @method $this whereNotIn($column_name, $values)
  70. * @method static $this whereNotIn($column_name, $values)
  71. * @method $this whereNull($column_name)
  72. * @method static static whereNull($column_name)
  73. * @method $this whereNotNull($column_name)
  74. * @method static $this whereNotNull($column_name)
  75. * @method $this whereRaw($clause, $parameters=array())
  76. * @method static $this whereRaw($clause, $parameters=array())
  77. * @method $this deleteMany()
  78. * @method static $this deleteMany()
  79. * @method $this orderByDesc($column_name)
  80. * @method static $this orderByDesc($column_name)
  81. * @method $this orderByAsc($column_name)
  82. * @method static $this orderByAsc($column_name)
  83. * @method $this orderByExpr($clause)
  84. * @method $this groupBy($column_name)
  85. * @method $this groupByExpr($expr)
  86. * @method $this havingEqual($column_name, $value=null)
  87. * @method $this havingNotEqual($column_name, $value=null)
  88. * @method $this havingIdIs($id)
  89. * @method $this havingLike($column_name, $value=null)
  90. * @method $this havingNotLike($column_name, $value=null)
  91. * @method $this havingGt($column_name, $value=null)
  92. * @method $this havingLt($column_name, $value=null)
  93. * @method $this havingGte($column_name, $value=null)
  94. * @method $this havingLte($column_name, $value=null)
  95. * @method $this havingIn($column_name, $values=null)
  96. * @method $this havingNotIn($column_name, $values=null)
  97. * @method $this havingNull($column_name)
  98. * @method $this havingNotNull($column_name)
  99. * @method $this havingRaw($clause, $parameters=array())
  100. * @method static $this clearCache($table_name = null, $connection_name = self::DEFAULT_CONNECTION)
  101. * @method bool setExpr($key, $value = null)
  102. * @method bool isDirty($key)
  103. * @method static static filter(...$args)
  104. * @method array asArray(...$args)
  105. * @method $this hasMany($associated_class_name, $foreign_key_name=null, $foreign_key_name_in_current_models_table=null, $connection_name=null)
  106. * @method $this hasManyThrough($associated_class_name, $join_class_name=null, $key_to_base_table=null, $key_to_associated_table=null, $key_in_base_table=null, $key_in_associated_table=null, $connection_name=null)
  107. * @method mixed hasOne($associated_class_name, $foreign_key_name=null, $foreign_key_name_in_current_models_table=null, $connection_name=null)
  108. * @method $this|bool create($data=null)
  109. * @method static $this|bool create($data=null)
  110. * @method int count()
  111. * @method static int count()
  112. * @method int sum($column_name)
  113. * @method int min($column_name)
  114. * @method int max($column_name)
  115. * @method int avg($column_name)
  116. * @method static int sum($column_name)
  117. * @method static int min($column_name)
  118. * @method static int max($column_name)
  119. * @method static int avg($column_name)
  120. * @method static static limit(int $limit)
  121. * @method static static distinct()
  122. * @method $this set(string|array $key, string|null $value = null)
  123. *
  124. * @property string|null $createdAt
  125. * @property string|null $updatedAt
  126. * @property string|null $id
  127. * @property string|null $first
  128. * @property string|null $last
  129. */
  130. class Model extends \MailPoetVendor\Sudzy\ValidModel {
  131. const DUPLICATE_RECORD = 23000;
  132. public static $_table; // phpcs:ignore PSR2.Classes.PropertyDeclaration
  133. protected $_errors; // phpcs:ignore PSR2.Classes.PropertyDeclaration
  134. protected $newRecord;
  135. public function __construct() {
  136. $this->_errors = [];
  137. $validator = new ModelValidator();
  138. parent::__construct($validator);
  139. }
  140. /**
  141. * @return static
  142. */
  143. public static function create() {
  144. $created = parent::create();
  145. if (is_bool($created)) {
  146. throw new \Exception('ORM is not initialised');
  147. }
  148. return $created;
  149. }
  150. /**
  151. * Creates a row, or updates it if already exists. It tries to find the existing
  152. * row by `id` (if given in `$data`), or by the given `$keys`. If `$onCreate` is
  153. * given, it's used to transform `$data` before creating the new row.
  154. *
  155. * @param array $data
  156. * @param array|bool $keys
  157. * @param callable|bool $onCreate
  158. * @return self
  159. */
  160. static protected function _createOrUpdate($data = [], $keys = false, $onCreate = false) {
  161. $model = false;
  162. if (isset($data['id']) && (int)$data['id'] > 0) {
  163. $model = static::findOne((int)$data['id']);
  164. }
  165. if ($model === false && !empty($keys) && is_array($keys)) {
  166. foreach ($keys as $field => $value) {
  167. if ($model === false) {
  168. $model = static::where($field, $value);
  169. } else {
  170. $model = $model->where($field, $value);
  171. }
  172. }
  173. if ($model !== false) $model = $model->findOne();
  174. }
  175. if ($model === false) {
  176. if (!empty($onCreate) && is_callable($onCreate)) {
  177. $data = $onCreate($data);
  178. }
  179. $model = static::create();
  180. $model->hydrate($data);
  181. } else {
  182. unset($data['id']);
  183. $model->set($data);
  184. }
  185. return $model->save();
  186. }
  187. static public function createOrUpdate($data = []) {
  188. return self::_createOrUpdate($data);
  189. }
  190. public function getErrors() {
  191. if (empty($this->_errors)) {
  192. return false;
  193. } else {
  194. return $this->_errors;
  195. }
  196. }
  197. public function setError($error = '', $errorCode = null) {
  198. if (!$errorCode) {
  199. $errorCode = count($this->_errors);
  200. }
  201. if (!empty($error)) {
  202. if (is_array($error)) {
  203. $this->_errors = array_merge($this->_errors, $error);
  204. $this->_errors = array_unique($this->_errors);
  205. } else {
  206. $this->_errors[$errorCode] = $error;
  207. }
  208. }
  209. }
  210. /**
  211. * @return static
  212. * @phpstan-ignore-next-line Our Model has incompatible return type with parent
  213. */
  214. public function save() {
  215. $this->setTimestamp();
  216. $this->newRecord = $this->isNew();
  217. try {
  218. parent::save();
  219. } catch (\MailPoetVendor\Sudzy\ValidationException $e) {
  220. $this->setError($e->getValidationErrors());
  221. } catch (\PDOException $e) {
  222. switch ($e->getCode()) {
  223. case 23000:
  224. preg_match("/for key '(?:.*\.)*(.*?)'/i", $e->getMessage(), $matches);
  225. if (isset($matches[1])) {
  226. $column = $matches[1];
  227. $this->setError(
  228. sprintf(
  229. WPFunctions::get()->__('Another record already exists. Please specify a different "%1$s".', 'mailpoet'),
  230. $column
  231. ),
  232. Model::DUPLICATE_RECORD
  233. );
  234. } else {
  235. $this->setError($e->getMessage());
  236. }
  237. break;
  238. default:
  239. $this->setError($e->getMessage());
  240. }
  241. }
  242. return $this;
  243. }
  244. public function isNew() {
  245. return (isset($this->newRecord)) ?
  246. $this->newRecord :
  247. parent::isNew();
  248. }
  249. public function trash() {
  250. return $this->set_expr('deleted_at', 'NOW()')->save();
  251. }
  252. public static function bulkTrash($orm) {
  253. $model = get_called_class();
  254. $count = self::bulkAction($orm, function($ids) use ($model) {
  255. $model::rawExecute(join(' ', [
  256. 'UPDATE `' . $model::$_table . '`',
  257. 'SET `deleted_at` = NOW()',
  258. 'WHERE `id` IN (' . rtrim(str_repeat('?,', count($ids)), ',') . ')',
  259. ]), $ids);
  260. });
  261. return ['count' => $count];
  262. }
  263. public static function bulkDelete($orm) {
  264. $model = get_called_class();
  265. $count = self::bulkAction($orm, function($ids) use ($model) {
  266. $model::whereIn('id', $ids)->deleteMany();
  267. });
  268. return ['count' => $count];
  269. }
  270. public function restore() {
  271. return $this->set_expr('deleted_at', 'NULL')->save();
  272. }
  273. public static function bulkRestore($orm) {
  274. $model = get_called_class();
  275. $count = self::bulkAction($orm, function($ids) use ($model) {
  276. $model::rawExecute(join(' ', [
  277. 'UPDATE `' . $model::$_table . '`',
  278. 'SET `deleted_at` = NULL',
  279. 'WHERE `id` IN (' . rtrim(str_repeat('?,', count($ids)), ',') . ')',
  280. ]), $ids);
  281. });
  282. return ['count' => $count];
  283. }
  284. public static function bulkAction($orm, $callback = false) {
  285. $total = $orm->count();
  286. if ($total === 0) return false;
  287. $rows = $orm->select(static::$_table . '.id')
  288. ->offset(null)
  289. ->limit(null)
  290. ->findArray();
  291. $ids = array_map(function($model) {
  292. return (int)$model['id'];
  293. }, $rows);
  294. if (is_callable($callback)) {
  295. $callback($ids);
  296. }
  297. // get number of affected rows
  298. return $orm->get_last_statement()
  299. ->rowCount();
  300. }
  301. public function duplicate($data = []) {
  302. $model = get_called_class();
  303. $modelData = array_merge($this->asArray(), $data);
  304. unset($modelData['id']);
  305. $duplicate = $model::create();
  306. $duplicate->hydrate($modelData);
  307. $duplicate->set_expr('created_at', 'NOW()');
  308. $duplicate->set_expr('updated_at', 'NOW()');
  309. if (isset($modelData['deleted_at'])) {
  310. $duplicate->set_expr('deleted_at', 'NULL');
  311. }
  312. $duplicate->save();
  313. return $duplicate;
  314. }
  315. public function setTimestamp() {
  316. if ($this->createdAt === null) {
  317. $this->set_expr('created_at', 'NOW()');
  318. }
  319. }
  320. public static function getPublished() {
  321. return static::whereNull('deleted_at');
  322. }
  323. public static function getTrashed() {
  324. return static::whereNotNull('deleted_at');
  325. }
  326. /**
  327. * Rethrow PDOExceptions to prevent exposing sensitive data in stack traces
  328. */
  329. public static function __callStatic($method, $parameters) {
  330. try {
  331. return parent::__callStatic($method, $parameters);
  332. } catch (\PDOException $e) {
  333. throw new \Exception($e->getMessage());
  334. }
  335. }
  336. public function validate() {
  337. $success = true;
  338. foreach (array_keys($this->_validations) as $field) {
  339. $success = $success && $this->validateField($field, $this->$field);
  340. }
  341. $this->setError($this->getValidationErrors());
  342. return $success;
  343. }
  344. public function __get($name) {
  345. $value = parent::__get($name);
  346. if ($value !== null) {
  347. return $value;
  348. }
  349. $name = Helpers::camelCaseToUnderscore($name);
  350. return parent::__get($name);
  351. }
  352. public function __set($name, $value) {
  353. $name = Helpers::camelCaseToUnderscore($name);
  354. parent::__set($name, $value);
  355. }
  356. public function __isset($name) {
  357. $name = Helpers::camelCaseToUnderscore($name);
  358. return parent::__isset($name);
  359. }
  360. }