Model.php 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Database\Model;
  12. use ArrayAccess;
  13. use Exception;
  14. use Hyperf\Collection\Arr;
  15. use Hyperf\Collection\Collection as BaseCollection;
  16. use Hyperf\Contract\Arrayable;
  17. use Hyperf\Contract\CompressInterface;
  18. use Hyperf\Contract\Jsonable;
  19. use Hyperf\Contract\UnCompressInterface;
  20. use Hyperf\Database\ConnectionInterface;
  21. use Hyperf\Database\Model\Relations\Pivot;
  22. use Hyperf\Database\Query\Builder as QueryBuilder;
  23. use Hyperf\Stringable\Str;
  24. use Hyperf\Stringable\StrCache;
  25. use JsonSerializable;
  26. use Psr\EventDispatcher\EventDispatcherInterface;
  27. use Psr\EventDispatcher\StoppableEventInterface;
  28. use Throwable;
  29. use function Hyperf\Collection\collect;
  30. use function Hyperf\Collection\last;
  31. use function Hyperf\Support\class_basename;
  32. use function Hyperf\Support\class_uses_recursive;
  33. use function Hyperf\Tappable\tap;
  34. /**
  35. * @mixin ModelIDE
  36. */
  37. abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, CompressInterface
  38. {
  39. use Concerns\HasAttributes;
  40. use Concerns\HasEvents;
  41. use Concerns\HasGlobalScopes;
  42. use Concerns\HasRelationships;
  43. use Concerns\HasTimestamps;
  44. use Concerns\HidesAttributes;
  45. use Concerns\GuardsAttributes;
  46. /**
  47. * The name of the "created at" column.
  48. *
  49. * @var null|string
  50. */
  51. public const CREATED_AT = 'created_at';
  52. /**
  53. * The name of the "updated at" column.
  54. *
  55. * @var null|string
  56. */
  57. public const UPDATED_AT = 'updated_at';
  58. /**
  59. * Indicates if the IDs are auto-incrementing.
  60. */
  61. public bool $incrementing = true;
  62. /**
  63. * Indicates if the model exists.
  64. */
  65. public bool $exists = false;
  66. /**
  67. * Indicates if the model was inserted during the current request lifecycle.
  68. */
  69. public bool $wasRecentlyCreated = false;
  70. protected array $guarded = ['*'];
  71. /**
  72. * The connection name for the model.
  73. */
  74. protected ?string $connection = 'default';
  75. /**
  76. * The table associated with the model.
  77. */
  78. protected ?string $table = null;
  79. /**
  80. * The primary key for the model.
  81. */
  82. protected string $primaryKey = 'id';
  83. /**
  84. * The "type" of the auto-incrementing ID.
  85. */
  86. protected string $keyType = 'int';
  87. /**
  88. * The relations to eager load on every query.
  89. */
  90. protected array $with = [];
  91. /**
  92. * The relationship counts that should be eager loaded on every query.
  93. */
  94. protected array $withCount = [];
  95. /**
  96. * The number of models to return for pagination.
  97. */
  98. protected int $perPage = 15;
  99. /**
  100. * The array of trait initializers that will be called on each new instance.
  101. */
  102. protected array $traitInitializers = [];
  103. /**
  104. * Create a new Model model instance.
  105. */
  106. public function __construct(array $attributes = [])
  107. {
  108. $this->bootIfNotBooted();
  109. $this->initializeTraits();
  110. $this->syncOriginal();
  111. $this->fill($attributes);
  112. }
  113. /**
  114. * Dynamically retrieve attributes on the model.
  115. *
  116. * @param string $key
  117. */
  118. public function __get($key)
  119. {
  120. return $this->getAttribute($key);
  121. }
  122. /**
  123. * Dynamically set attributes on the model.
  124. *
  125. * @param string $key
  126. * @param mixed $value
  127. */
  128. public function __set($key, $value)
  129. {
  130. $this->setAttribute($key, $value);
  131. }
  132. /**
  133. * Determine if an attribute or relation exists on the model.
  134. *
  135. * @param string $key
  136. * @return bool
  137. */
  138. public function __isset($key)
  139. {
  140. return $this->offsetExists($key);
  141. }
  142. /**
  143. * Unset an attribute on the model.
  144. *
  145. * @param string $key
  146. */
  147. public function __unset($key)
  148. {
  149. $this->offsetUnset($key);
  150. }
  151. /**
  152. * Handle dynamic method calls into the model.
  153. *
  154. * @param string $method
  155. * @param array $parameters
  156. */
  157. public function __call($method, $parameters)
  158. {
  159. if (in_array($method, ['increment', 'decrement'])) {
  160. return $this->{$method}(...$parameters);
  161. }
  162. if ($resolver = $this->relationResolver(static::class, $method)) {
  163. return $resolver($this);
  164. }
  165. return $this->newQuery()->{$method}(...$parameters);
  166. }
  167. /**
  168. * Handle dynamic static method calls into the method.
  169. *
  170. * @param string $method
  171. * @param array $parameters
  172. */
  173. public static function __callStatic($method, $parameters)
  174. {
  175. return (new static())->{$method}(...$parameters);
  176. }
  177. /**
  178. * Convert the model to its string representation.
  179. */
  180. public function __toString(): string
  181. {
  182. return $this->toJson();
  183. }
  184. /**
  185. * Prepare the object for serialization.
  186. *
  187. * @return array
  188. */
  189. public function __sleep()
  190. {
  191. $this->mergeAttributesFromClassCasts();
  192. $this->classCastCache = [];
  193. return array_keys(get_object_vars($this));
  194. }
  195. /**
  196. * When a model is being unserialized, check if it needs to be booted.
  197. */
  198. public function __wakeup()
  199. {
  200. $this->bootIfNotBooted();
  201. }
  202. /**
  203. * Disables relationship model touching for the current class during given callback scope.
  204. */
  205. public static function withoutTouching(callable $callback)
  206. {
  207. static::withoutTouchingOn([static::class], $callback);
  208. }
  209. /**
  210. * Disables relationship model touching for the given model classes during given callback scope.
  211. */
  212. public static function withoutTouchingOn(array $models, callable $callback)
  213. {
  214. IgnoreOnTouch::$container = array_values(array_merge(IgnoreOnTouch::$container, $models));
  215. try {
  216. $callback();
  217. } finally {
  218. IgnoreOnTouch::$container = array_values(array_diff(IgnoreOnTouch::$container, $models));
  219. }
  220. }
  221. /**
  222. * Determine if the given model is ignoring touches.
  223. *
  224. * @param null|string $class
  225. * @return bool
  226. */
  227. public static function isIgnoringTouch($class = null)
  228. {
  229. $class = $class ?: static::class;
  230. foreach (IgnoreOnTouch::$container as $ignoredClass) {
  231. if ($class === $ignoredClass || is_subclass_of($class, $ignoredClass)) {
  232. return true;
  233. }
  234. }
  235. return false;
  236. }
  237. /**
  238. * Fill the model with an array of attributes.
  239. *
  240. * @return $this
  241. * @throws MassAssignmentException
  242. */
  243. public function fill(array $attributes)
  244. {
  245. $totallyGuarded = $this->totallyGuarded();
  246. foreach ($this->fillableFromArray($attributes) as $key => $value) {
  247. $key = $this->removeTableFromKey($key);
  248. // The developers may choose to place some attributes in the "fillable" array
  249. // which means only those attributes may be set through mass assignment to
  250. // the model, and all others will just get ignored for security reasons.
  251. if ($this->isFillable($key)) {
  252. $this->setAttribute($key, $value);
  253. } elseif ($totallyGuarded) {
  254. throw new MassAssignmentException(sprintf('Add [%s] to fillable property to allow mass assignment on [%s].', $key, get_class($this)));
  255. }
  256. }
  257. return $this;
  258. }
  259. /**
  260. * Fill the model with an array of attributes. Force mass assignment.
  261. *
  262. * @return $this
  263. */
  264. public function forceFill(array $attributes)
  265. {
  266. return static::unguarded(function () use ($attributes) {
  267. return $this->fill($attributes);
  268. });
  269. }
  270. /**
  271. * Qualify the given column name by the model's table.
  272. *
  273. * @param string $column
  274. * @return string
  275. */
  276. public function qualifyColumn($column)
  277. {
  278. if (Str::contains($column, '.')) {
  279. return $column;
  280. }
  281. return $this->getTable() . '.' . $column;
  282. }
  283. /**
  284. * Create a new instance of the given model.
  285. *
  286. * @param array $attributes
  287. * @param bool $exists
  288. * @return static
  289. */
  290. public function newInstance($attributes = [], $exists = false)
  291. {
  292. // This method just provides a convenient way for us to generate fresh model
  293. // instances of this current model. It is particularly useful during the
  294. // hydration of new objects via the Model query builder instances.
  295. $model = new static((array) $attributes);
  296. $model->exists = $exists;
  297. $model->setConnection($this->getConnectionName());
  298. $model->setTable($this->getTable());
  299. $model->mergeCasts($this->casts);
  300. return $model;
  301. }
  302. /**
  303. * Create a new model instance that is existing.
  304. *
  305. * @param array $attributes
  306. * @param null|string $connection
  307. * @return static
  308. */
  309. public function newFromBuilder($attributes = [], $connection = null)
  310. {
  311. $model = $this->newInstance([], true);
  312. $model->setRawAttributes((array) $attributes, true);
  313. $model->setConnection($connection ?: $this->getConnectionName());
  314. $model->fireModelEvent('retrieved');
  315. return $model;
  316. }
  317. /**
  318. * Begin querying the model on a given connection.
  319. *
  320. * @param null|string $connection
  321. * @return Builder
  322. */
  323. public static function on($connection = null)
  324. {
  325. // First we will just create a fresh instance of this model, and then we can set the
  326. // connection on the model so that it is used for the queries we execute, as well
  327. // as being set on every relation we retrieve without a custom connection name.
  328. $instance = new static();
  329. $instance->setConnection($connection);
  330. return $instance->newQuery();
  331. }
  332. /**
  333. * Begin querying the model on the write connection.
  334. *
  335. * @return QueryBuilder
  336. */
  337. public static function onWriteConnection()
  338. {
  339. return static::query()->useWritePdo();
  340. }
  341. /**
  342. * Get all of the models from the database.
  343. *
  344. * @param array|mixed $columns
  345. * @return Collection|static[]
  346. */
  347. public static function all($columns = ['*'])
  348. {
  349. return static::query()->get(is_array($columns) ? $columns : func_get_args());
  350. }
  351. /**
  352. * Begin querying a model with eager loading.
  353. *
  354. * @param array|string $relations
  355. * @return Builder|static
  356. */
  357. public static function with($relations)
  358. {
  359. return static::query()->with(is_string($relations) ? func_get_args() : $relations);
  360. }
  361. /**
  362. * Eager load relations on the model.
  363. *
  364. * @param array|string $relations
  365. * @return $this
  366. */
  367. public function load($relations)
  368. {
  369. $query = $this->newQueryWithoutRelationships()->with(is_string($relations) ? func_get_args() : $relations);
  370. $query->eagerLoadRelations([$this]);
  371. return $this;
  372. }
  373. /**
  374. * Eager load relationships on the polymorphic relation of a model.
  375. *
  376. * @param string $relation
  377. * @param array $relations
  378. * @return $this
  379. */
  380. public function loadMorph($relation, $relations)
  381. {
  382. $className = get_class($this->{$relation});
  383. $this->{$relation}->load($relations[$className] ?? []);
  384. return $this;
  385. }
  386. /**
  387. * Eager load relations on the model if they are not already eager loaded.
  388. *
  389. * @param array|string $relations
  390. * @return $this
  391. */
  392. public function loadMissing($relations)
  393. {
  394. $relations = is_string($relations) ? func_get_args() : $relations;
  395. $this->newCollection([$this])->loadMissing($relations);
  396. return $this;
  397. }
  398. /**
  399. * Eager load relation counts on the model.
  400. *
  401. * @param array|string $relations
  402. * @return $this
  403. */
  404. public function loadCount($relations)
  405. {
  406. $relations = is_string($relations) ? func_get_args() : $relations;
  407. $this->newCollection([$this])->loadCount($relations);
  408. return $this;
  409. }
  410. /**
  411. * Eager load relationship counts on the polymorphic relation of a model.
  412. *
  413. * @param string $relation
  414. * @param array $relations
  415. * @return $this
  416. */
  417. public function loadMorphCount($relation, $relations)
  418. {
  419. $className = get_class($this->{$relation});
  420. $this->{$relation}->loadCount($relations[$className] ?? []);
  421. return $this;
  422. }
  423. /**
  424. * Update the model in the database.
  425. *
  426. * @return bool
  427. */
  428. public function update(array $attributes = [], array $options = [])
  429. {
  430. if (! $this->exists) {
  431. return false;
  432. }
  433. return $this->fill($attributes)->save($options);
  434. }
  435. /**
  436. * Save the model and all of its relationships.
  437. *
  438. * @return bool
  439. */
  440. public function push()
  441. {
  442. if (! $this->save()) {
  443. return false;
  444. }
  445. // To sync all of the relationships to the database, we will simply spin through
  446. // the relationships and save each model via this "push" method, which allows
  447. // us to recurse into all of these nested relations for the model instance.
  448. foreach ($this->relations as $models) {
  449. $models = $models instanceof Collection ? $models->all() : [$models];
  450. foreach (array_filter($models) as $model) {
  451. if (! $model->push()) {
  452. return false;
  453. }
  454. }
  455. }
  456. return true;
  457. }
  458. /**
  459. * Save the model to the database.
  460. */
  461. public function save(array $options = []): bool
  462. {
  463. $this->mergeAttributesFromClassCasts();
  464. $query = $this->newModelQuery();
  465. // If the "saving" event returns false we'll bail out of the save and return
  466. // false, indicating that the save failed. This provides a chance for any
  467. // listeners to cancel save operations if validations fail or whatever.
  468. if ($saving = $this->fireModelEvent('saving')) {
  469. if ($saving instanceof StoppableEventInterface && $saving->isPropagationStopped()) {
  470. return false;
  471. }
  472. }
  473. // If the model already exists in the database we can just update our record
  474. // that is already in this database using the current IDs in this "where"
  475. // clause to only update this model. Otherwise, we'll just insert them.
  476. if ($this->exists) {
  477. $saved = $this->isDirty() ? $this->performUpdate($query) : true;
  478. } else {
  479. // If the model is brand new, we'll insert it into our database and set the
  480. // ID attribute on the model to the value of the newly inserted row's ID
  481. // which is typically an auto-increment value managed by the database.
  482. $saved = $this->performInsert($query);
  483. if (! $this->getConnectionName() && $connection = $query->getConnection()) {
  484. $this->setConnection($connection->getName());
  485. }
  486. }
  487. // If the model is successfully saved, we need to do a few more things once
  488. // that is done. We will call the "saved" method here to run any actions
  489. // we need to happen after a model gets successfully saved right here.
  490. if ($saved) {
  491. $this->finishSave($options);
  492. }
  493. return $saved;
  494. }
  495. /**
  496. * Save the model to the database using transaction.
  497. *
  498. * @return bool
  499. * @throws Throwable
  500. */
  501. public function saveOrFail(array $options = [])
  502. {
  503. return $this->getConnection()->transaction(function () use ($options) {
  504. return $this->save($options);
  505. });
  506. }
  507. /**
  508. * Destroy the models for the given IDs.
  509. *
  510. * @param array|BaseCollection|int $ids
  511. */
  512. public static function destroy($ids): int
  513. {
  514. // We'll initialize a count here so we will return the total number of deletes
  515. // for the operation. The developers can then check this number as a boolean
  516. // type value or get this total count of records deleted for logging, etc.
  517. $count = 0;
  518. if ($ids instanceof BaseCollection) {
  519. $ids = $ids->all();
  520. }
  521. $ids = is_array($ids) ? $ids : func_get_args();
  522. // We will actually pull the models from the database table and call delete on
  523. // each of them individually so that their events get fired properly with a
  524. // correct set of attributes in case the developers wants to check these.
  525. $key = ($instance = new static())->getKeyName();
  526. foreach ($instance->whereIn($key, $ids)->get() as $model) {
  527. if ($model->delete()) {
  528. ++$count;
  529. }
  530. }
  531. return $count;
  532. }
  533. /**
  534. * Delete the model from the database.
  535. *
  536. * @return null|bool
  537. * @throws Exception
  538. */
  539. public function delete()
  540. {
  541. $this->mergeAttributesFromClassCasts();
  542. if (is_null($this->getKeyName())) {
  543. throw new Exception('No primary key defined on model.');
  544. }
  545. // If the model doesn't exist, there is nothing to delete so we'll just return
  546. // immediately and not do anything else. Otherwise, we will continue with a
  547. // deletion process on the model, firing the proper events, and so forth.
  548. if (! $this->exists) {
  549. return;
  550. }
  551. if ($event = $this->fireModelEvent('deleting')) {
  552. if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
  553. return false;
  554. }
  555. }
  556. // Here, we'll touch the owning models, verifying these timestamps get updated
  557. // for the models. This will allow any caching to get broken on the parents
  558. // by the timestamp. Then we will go ahead and delete the model instance.
  559. $this->touchOwners();
  560. $this->performDeleteOnModel();
  561. // Once the model has been deleted, we will fire off the deleted event so that
  562. // the developers may hook into post-delete operations. We will then return
  563. // a boolean true as the delete is presumably successful on the database.
  564. $this->fireModelEvent('deleted');
  565. return true;
  566. }
  567. /**
  568. * Force a hard delete on a soft deleted model.
  569. * This method protects developers from running forceDelete when trait is missing.
  570. *
  571. * @return null|bool
  572. */
  573. public function forceDelete()
  574. {
  575. return $this->delete();
  576. }
  577. /**
  578. * Begin querying the model.
  579. *
  580. * @return Builder
  581. */
  582. public static function query()
  583. {
  584. return (new static())->newQuery();
  585. }
  586. /**
  587. * Get a new query builder for the model's table.
  588. *
  589. * @return Builder
  590. */
  591. public function newQuery()
  592. {
  593. return $this->registerGlobalScopes($this->newQueryWithoutScopes());
  594. }
  595. /**
  596. * Get a new query builder that doesn't have any global scopes or eager loading.
  597. *
  598. * @return Builder|static
  599. */
  600. public function newModelQuery()
  601. {
  602. return $this->newModelBuilder($this->newBaseQueryBuilder())->setModel($this);
  603. }
  604. /**
  605. * Get a new query builder with no relationships loaded.
  606. *
  607. * @return Builder
  608. */
  609. public function newQueryWithoutRelationships()
  610. {
  611. return $this->registerGlobalScopes($this->newModelQuery());
  612. }
  613. /**
  614. * Register the global scopes for this builder instance.
  615. *
  616. * @param Builder $builder
  617. * @return Builder
  618. */
  619. public function registerGlobalScopes($builder)
  620. {
  621. foreach ($this->getGlobalScopes() as $identifier => $scope) {
  622. $builder->withGlobalScope($identifier, $scope);
  623. }
  624. return $builder;
  625. }
  626. /**
  627. * Get a new query builder that doesn't have any global scopes.
  628. *
  629. * @return Builder|static
  630. */
  631. public function newQueryWithoutScopes()
  632. {
  633. return $this->newModelQuery()->with($this->with)->withCount($this->withCount);
  634. }
  635. /**
  636. * Get a new query instance without a given scope.
  637. *
  638. * @param Scope|string $scope
  639. * @return Builder
  640. */
  641. public function newQueryWithoutScope($scope)
  642. {
  643. return $this->newQuery()->withoutGlobalScope($scope);
  644. }
  645. /**
  646. * Get a new query to restore one or more models by their queueable IDs.
  647. *
  648. * @param array|int $ids
  649. * @return Builder
  650. */
  651. public function newQueryForRestoration($ids)
  652. {
  653. return is_array($ids) ? $this->newQueryWithoutScopes()
  654. ->whereIn($this->getQualifiedKeyName(), $ids) : $this->newQueryWithoutScopes()->whereKey($ids);
  655. }
  656. /**
  657. * Create a new Model query builder for the model.
  658. *
  659. * @param QueryBuilder $query
  660. * @return Builder|static
  661. */
  662. public function newModelBuilder($query)
  663. {
  664. return new Builder($query);
  665. }
  666. /**
  667. * Create a new Model Collection instance.
  668. *
  669. * @return Collection
  670. */
  671. public function newCollection(array $models = [])
  672. {
  673. return new Collection($models);
  674. }
  675. /**
  676. * Create a new pivot model instance.
  677. *
  678. * @param string $table
  679. * @param bool $exists
  680. * @param null|string $using
  681. * @return Pivot
  682. */
  683. public function newPivot(self $parent, array $attributes, $table, $exists, $using = null)
  684. {
  685. return $using ? $using::fromRawAttributes($parent, $attributes, $table, $exists) : Pivot::fromAttributes($parent, $attributes, $table, $exists);
  686. }
  687. /**
  688. * Convert the model instance to an array.
  689. */
  690. public function toArray(): array
  691. {
  692. return array_merge($this->attributesToArray(), $this->relationsToArray());
  693. }
  694. /**
  695. * Convert the model instance to JSON.
  696. *
  697. * @param int $options
  698. * @return string
  699. * @throws JsonEncodingException
  700. */
  701. public function toJson($options = 0)
  702. {
  703. $json = json_encode($this->jsonSerialize(), $options);
  704. if (json_last_error() !== JSON_ERROR_NONE) {
  705. throw JsonEncodingException::forModel($this, json_last_error_msg());
  706. }
  707. return $json;
  708. }
  709. /**
  710. * Convert the object into something JSON serializable.
  711. */
  712. public function jsonSerialize(): mixed
  713. {
  714. return $this->toArray();
  715. }
  716. /**
  717. * Reload a fresh model instance from the database.
  718. *
  719. * @param array|string $with
  720. * @return null|static
  721. */
  722. public function fresh($with = [])
  723. {
  724. if (! $this->exists) {
  725. return;
  726. }
  727. return static::newQueryWithoutScopes()
  728. ->with(is_string($with) ? func_get_args() : $with)
  729. ->where($this->getKeyName(), $this->getKey())
  730. ->first();
  731. }
  732. /**
  733. * Reload the current model instance with fresh attributes from the database.
  734. *
  735. * @return $this
  736. */
  737. public function refresh()
  738. {
  739. if (! $this->exists) {
  740. return $this;
  741. }
  742. $this->setRawAttributes(static::newQueryWithoutScopes()->findOrFail($this->getKey())->attributes);
  743. $this->load(collect($this->relations)->except('pivot')->keys()->toArray());
  744. $this->syncOriginal();
  745. return $this;
  746. }
  747. /**
  748. * Clone the model into a new, non-existing instance.
  749. *
  750. * @return static
  751. */
  752. public function replicate(?array $except = null)
  753. {
  754. $defaults = [
  755. $this->getKeyName(),
  756. $this->getCreatedAtColumn(),
  757. $this->getUpdatedAtColumn(),
  758. ];
  759. $attributes = Arr::except($this->getAttributes(), $except ? array_unique(array_merge($except, $defaults)) : $defaults);
  760. return tap(new static(), function ($instance) use ($attributes) {
  761. // @var \Hyperf\Database\Model\Model $instance
  762. $instance->setRawAttributes($attributes);
  763. $instance->setRelations($this->relations);
  764. });
  765. }
  766. /**
  767. * Determine if two models have the same ID and belong to the same table.
  768. *
  769. * @param null|Model $model
  770. * @return bool
  771. */
  772. public function is($model)
  773. {
  774. return ! is_null($model) && $this->getKey() === $model->getKey() && $this->getTable() === $model->getTable() && $this->getConnectionName() === $model->getConnectionName();
  775. }
  776. /**
  777. * Determine if two models are not the same.
  778. *
  779. * @param null|Model $model
  780. * @return bool
  781. */
  782. public function isNot($model)
  783. {
  784. return ! $this->is($model);
  785. }
  786. /**
  787. * Get the database connection for the model.
  788. * You can write it by yourself.
  789. */
  790. public function getConnection(): ConnectionInterface
  791. {
  792. return Register::resolveConnection($this->getConnectionName());
  793. }
  794. /**
  795. * Get the event dispatcher for the model.
  796. * You can write it by yourself.
  797. */
  798. public function getEventDispatcher(): ?EventDispatcherInterface
  799. {
  800. return Register::getEventDispatcher();
  801. }
  802. /**
  803. * Get the current connection name for the model.
  804. *
  805. * @return string
  806. */
  807. public function getConnectionName()
  808. {
  809. return $this->connection;
  810. }
  811. /**
  812. * Set the connection associated with the model.
  813. *
  814. * @param string $name
  815. * @return $this
  816. */
  817. public function setConnection($name)
  818. {
  819. $this->connection = $name;
  820. return $this;
  821. }
  822. /**
  823. * Get the table associated with the model.
  824. */
  825. public function getTable(): string
  826. {
  827. return $this->table ?? StrCache::snake(Str::pluralStudly(class_basename($this)));
  828. }
  829. /**
  830. * Set the table associated with the model.
  831. *
  832. * @param string $table
  833. * @return $this
  834. */
  835. public function setTable($table)
  836. {
  837. $this->table = $table;
  838. return $this;
  839. }
  840. /**
  841. * Get the primary key for the model.
  842. *
  843. * @return string
  844. */
  845. public function getKeyName()
  846. {
  847. return $this->primaryKey;
  848. }
  849. /**
  850. * Set the primary key for the model.
  851. *
  852. * @param string $key
  853. * @return $this
  854. */
  855. public function setKeyName($key)
  856. {
  857. $this->primaryKey = $key;
  858. return $this;
  859. }
  860. /**
  861. * Get the table qualified key name.
  862. *
  863. * @return string
  864. */
  865. public function getQualifiedKeyName()
  866. {
  867. return $this->qualifyColumn($this->getKeyName());
  868. }
  869. /**
  870. * Get the auto-incrementing key type.
  871. *
  872. * @return string
  873. */
  874. public function getKeyType()
  875. {
  876. return $this->keyType;
  877. }
  878. /**
  879. * Set the data type for the primary key.
  880. *
  881. * @param string $type
  882. * @return $this
  883. */
  884. public function setKeyType($type)
  885. {
  886. $this->keyType = $type;
  887. return $this;
  888. }
  889. /**
  890. * Get the value indicating whether the IDs are incrementing.
  891. *
  892. * @return bool
  893. */
  894. public function getIncrementing()
  895. {
  896. return $this->incrementing;
  897. }
  898. /**
  899. * Set whether IDs are incrementing.
  900. *
  901. * @param bool $value
  902. * @return $this
  903. */
  904. public function setIncrementing($value)
  905. {
  906. $this->incrementing = $value;
  907. return $this;
  908. }
  909. /**
  910. * Get the value of the model's primary key.
  911. */
  912. public function getKey()
  913. {
  914. return $this->getAttribute($this->getKeyName());
  915. }
  916. /**
  917. * Get the value of the model's route key.
  918. */
  919. public function getRouteKey()
  920. {
  921. return $this->getAttribute($this->getRouteKeyName());
  922. }
  923. /**
  924. * Get the route key for the model.
  925. *
  926. * @return string
  927. */
  928. public function getRouteKeyName()
  929. {
  930. return $this->getKeyName();
  931. }
  932. /**
  933. * Retrieve the model for a bound value.
  934. *
  935. * @param mixed $value
  936. * @return null|Model
  937. */
  938. public function resolveRouteBinding($value)
  939. {
  940. return $this->where($this->getRouteKeyName(), $value)->first();
  941. }
  942. /**
  943. * Get the default foreign key name for the model.
  944. *
  945. * @return string
  946. */
  947. public function getForeignKey()
  948. {
  949. return StrCache::snake(class_basename($this)) . '_' . $this->getKeyName();
  950. }
  951. /**
  952. * Get the number of models to return per page.
  953. */
  954. public function getPerPage(): int
  955. {
  956. return $this->perPage;
  957. }
  958. /**
  959. * Set the number of models to return per page.
  960. *
  961. * @param int $perPage
  962. * @return $this
  963. */
  964. public function setPerPage($perPage)
  965. {
  966. $this->perPage = $perPage;
  967. return $this;
  968. }
  969. /**
  970. * Determine if the given attribute exists.
  971. */
  972. public function offsetExists(mixed $offset): bool
  973. {
  974. return ! is_null($this->getAttribute($offset));
  975. }
  976. /**
  977. * Get the value for a given offset.
  978. */
  979. public function offsetGet(mixed $offset): mixed
  980. {
  981. return $this->getAttribute($offset);
  982. }
  983. /**
  984. * Set the value for a given offset.
  985. */
  986. public function offsetSet(mixed $offset, mixed $value): void
  987. {
  988. $this->setAttribute($offset, $value);
  989. }
  990. /**
  991. * Unset the value for a given offset.
  992. */
  993. public function offsetUnset(mixed $offset): void
  994. {
  995. unset($this->attributes[$offset], $this->relations[$offset]);
  996. }
  997. public function compress(): UnCompressInterface
  998. {
  999. $key = $this->getKey();
  1000. $class = get_class($this);
  1001. return new ModelMeta($class, $key);
  1002. }
  1003. /**
  1004. * Check if the model needs to be booted and if so, do it.
  1005. */
  1006. protected function bootIfNotBooted(): void
  1007. {
  1008. $booted = Booted::$container[static::class] ?? false;
  1009. if (! $booted) {
  1010. Booted::$container[static::class] = true;
  1011. $this->fireModelEvent('booting');
  1012. $this->boot();
  1013. $this->fireModelEvent('booted');
  1014. }
  1015. }
  1016. /**
  1017. * The "booting" method of the model.
  1018. */
  1019. protected function boot(): void
  1020. {
  1021. $this->bootTraits();
  1022. }
  1023. /**
  1024. * Boot all of the bootable traits on the model.
  1025. */
  1026. protected function bootTraits(): void
  1027. {
  1028. $class = static::class;
  1029. $booted = [];
  1030. TraitInitializers::$container[$class] = [];
  1031. foreach (class_uses_recursive($class) as $trait) {
  1032. $method = 'boot' . class_basename($trait);
  1033. if (method_exists($class, $method) && ! in_array($method, $booted)) {
  1034. forward_static_call([$class, $method]);
  1035. $booted[] = $method;
  1036. }
  1037. if (method_exists($class, $method = 'initialize' . class_basename($trait))) {
  1038. TraitInitializers::$container[$class][] = $method;
  1039. TraitInitializers::$container[$class] = array_unique(TraitInitializers::$container[$class]);
  1040. }
  1041. }
  1042. }
  1043. /**
  1044. * Remove the table name from a given key.
  1045. */
  1046. protected function removeTableFromKey(string $key): string
  1047. {
  1048. return Str::contains($key, '.') ? last(explode('.', $key)) : $key;
  1049. }
  1050. /**
  1051. * Increment a column's value by a given amount.
  1052. *
  1053. * @param string $column
  1054. * @param float|int $amount
  1055. * @return int
  1056. */
  1057. protected function increment($column, $amount = 1, array $extra = [])
  1058. {
  1059. return $this->incrementOrDecrement($column, $amount, $extra, 'increment');
  1060. }
  1061. /**
  1062. * Decrement a column's value by a given amount.
  1063. *
  1064. * @param string $column
  1065. * @param float|int $amount
  1066. * @return int
  1067. */
  1068. protected function decrement($column, $amount = 1, array $extra = [])
  1069. {
  1070. return $this->incrementOrDecrement($column, $amount, $extra, 'decrement');
  1071. }
  1072. /**
  1073. * Run the increment or decrement method on the model.
  1074. *
  1075. * @param string $column
  1076. * @param float|int $amount
  1077. * @param array $extra
  1078. * @param string $method
  1079. * @return int
  1080. */
  1081. protected function incrementOrDecrement($column, $amount, $extra, $method)
  1082. {
  1083. $query = $this->newModelQuery();
  1084. if (! $this->exists) {
  1085. return $query->{$method}($column, $amount, $extra);
  1086. }
  1087. $this->{$column} = $this->{$column} + ($method === 'increment' ? $amount : $amount * -1);
  1088. $this->forceFill($extra);
  1089. $columns = array_merge(array_keys($extra), [$column]);
  1090. return tap($this->setKeysForSaveQuery($query)->{$method}($column, $amount, $extra), function () use ($columns) {
  1091. $this->syncChanges($columns);
  1092. $this->syncOriginalAttributes($columns);
  1093. });
  1094. }
  1095. /**
  1096. * Perform any actions that are necessary after the model is saved.
  1097. */
  1098. protected function finishSave(array $options)
  1099. {
  1100. $this->fireModelEvent('saved');
  1101. if ($this->isDirty() && ($options['touch'] ?? true)) {
  1102. $this->touchOwners();
  1103. }
  1104. $this->syncOriginal();
  1105. }
  1106. /**
  1107. * Perform a model update operation.
  1108. *
  1109. * @return bool
  1110. */
  1111. protected function performUpdate(Builder $query)
  1112. {
  1113. // If the updating event returns false, we will cancel the update operation so
  1114. // developers can hook Validation systems into their models and cancel this
  1115. // operation if the model does not pass validation. Otherwise, we update.
  1116. if ($event = $this->fireModelEvent('updating')) {
  1117. if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
  1118. return false;
  1119. }
  1120. }
  1121. // First we need to create a fresh query instance and touch the creation and
  1122. // update timestamp on the model which are maintained by us for developer
  1123. // convenience. Then we will just continue saving the model instances.
  1124. if ($this->usesTimestamps()) {
  1125. $this->updateTimestamps();
  1126. }
  1127. // Once we have run the update operation, we will fire the "updated" event for
  1128. // this model instance. This will allow developers to hook into these after
  1129. // models are updated, giving them a chance to do any special processing.
  1130. $dirty = $this->getDirty();
  1131. if (count($dirty) > 0) {
  1132. $this->setKeysForSaveQuery($query)->update($dirty);
  1133. $this->syncChanges();
  1134. $this->fireModelEvent('updated');
  1135. }
  1136. return true;
  1137. }
  1138. /**
  1139. * Set the keys for a save update query.
  1140. *
  1141. * @return Builder
  1142. */
  1143. protected function setKeysForSaveQuery(Builder $query)
  1144. {
  1145. $query->where($this->getKeyName(), '=', $this->getKeyForSaveQuery());
  1146. return $query;
  1147. }
  1148. /**
  1149. * Get the primary key value for a save query.
  1150. */
  1151. protected function getKeyForSaveQuery()
  1152. {
  1153. return $this->original[$this->getKeyName()] ?? $this->getKey();
  1154. }
  1155. /**
  1156. * Perform a model insert operation.
  1157. *
  1158. * @return bool
  1159. */
  1160. protected function performInsert(Builder $query)
  1161. {
  1162. if ($event = $this->fireModelEvent('creating')) {
  1163. if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
  1164. return false;
  1165. }
  1166. }
  1167. // First we'll need to create a fresh query instance and touch the creation and
  1168. // update timestamps on this model, which are maintained by us for developer
  1169. // convenience. After, we will just continue saving these model instances.
  1170. if ($this->usesTimestamps()) {
  1171. $this->updateTimestamps();
  1172. }
  1173. // If the model has an incrementing key, we can use the "insertGetId" method on
  1174. // the query builder, which will give us back the final inserted ID for this
  1175. // table from the database. Not all tables have to be incrementing though.
  1176. $attributes = $this->getAttributes();
  1177. if ($this->getIncrementing()) {
  1178. $this->insertAndSetId($query, $attributes);
  1179. }
  1180. // If the table isn't incrementing we'll simply insert these attributes as they
  1181. // are. These attribute arrays must contain an "id" column previously placed
  1182. // there by the developer as the manually determined key for these models.
  1183. else {
  1184. if (empty($attributes)) {
  1185. return true;
  1186. }
  1187. $query->insert($attributes);
  1188. }
  1189. // We will go ahead and set the exists property to true, so that it is set when
  1190. // the created event is fired, just in case the developer tries to update it
  1191. // during the event. This will allow them to do so and run an update here.
  1192. $this->exists = true;
  1193. $this->wasRecentlyCreated = true;
  1194. $this->fireModelEvent('created');
  1195. return true;
  1196. }
  1197. /**
  1198. * Insert the given attributes and set the ID on the model.
  1199. *
  1200. * @param array $attributes
  1201. */
  1202. protected function insertAndSetId(Builder $query, $attributes)
  1203. {
  1204. $id = $query->insertGetId($attributes, $keyName = $this->getKeyName());
  1205. $this->setAttribute($keyName, $id);
  1206. }
  1207. /**
  1208. * Perform the actual delete query on this model instance.
  1209. */
  1210. protected function performDeleteOnModel()
  1211. {
  1212. $this->setKeysForSaveQuery($this->newModelQuery())->delete();
  1213. $this->exists = false;
  1214. }
  1215. /**
  1216. * Get a new query builder instance for the connection.
  1217. *
  1218. * @return QueryBuilder
  1219. */
  1220. protected function newBaseQueryBuilder()
  1221. {
  1222. $connection = $this->getConnection();
  1223. return new QueryBuilder($connection, $connection->getQueryGrammar(), $connection->getPostProcessor());
  1224. }
  1225. /**
  1226. * Initialize any initializable traits on the model.
  1227. */
  1228. protected function initializeTraits(): void
  1229. {
  1230. foreach (TraitInitializers::$container[static::class] ?? [] as $method) {
  1231. $this->{$method}();
  1232. }
  1233. }
  1234. }