ModelSerializationTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <?php
  2. namespace Illuminate\Tests\Integration\Queue;
  3. use Illuminate\Database\Eloquent\Collection;
  4. use Illuminate\Database\Eloquent\Model;
  5. use Illuminate\Database\Eloquent\Relations\Pivot;
  6. use Illuminate\Database\Schema\Blueprint;
  7. use Illuminate\Queue\SerializesModels;
  8. use LogicException;
  9. use Orchestra\Testbench\TestCase;
  10. use Schema;
  11. class ModelSerializationTest extends TestCase
  12. {
  13. protected function getEnvironmentSetUp($app)
  14. {
  15. $app['config']->set('database.connections.custom', [
  16. 'driver' => 'sqlite',
  17. 'database' => ':memory:',
  18. 'prefix' => '',
  19. ]);
  20. }
  21. protected function setUp(): void
  22. {
  23. parent::setUp();
  24. Schema::create('users', function (Blueprint $table) {
  25. $table->increments('id');
  26. $table->string('email');
  27. });
  28. Schema::connection('custom')->create('users', function (Blueprint $table) {
  29. $table->increments('id');
  30. $table->string('email');
  31. });
  32. Schema::create('orders', function (Blueprint $table) {
  33. $table->increments('id');
  34. });
  35. Schema::create('lines', function (Blueprint $table) {
  36. $table->increments('id');
  37. $table->unsignedInteger('order_id');
  38. $table->unsignedInteger('product_id');
  39. });
  40. Schema::create('products', function (Blueprint $table) {
  41. $table->increments('id');
  42. });
  43. Schema::create('roles', function (Blueprint $table) {
  44. $table->increments('id');
  45. });
  46. Schema::create('role_user', function (Blueprint $table) {
  47. $table->unsignedInteger('user_id');
  48. $table->unsignedInteger('role_id');
  49. });
  50. }
  51. public function testItSerializeUserOnDefaultConnection()
  52. {
  53. $user = ModelSerializationTestUser::create([
  54. 'email' => 'mohamed@laravel.com',
  55. ]);
  56. ModelSerializationTestUser::create([
  57. 'email' => 'taylor@laravel.com',
  58. ]);
  59. $serialized = serialize(new ModelSerializationTestClass($user));
  60. $unSerialized = unserialize($serialized);
  61. $this->assertSame('testing', $unSerialized->user->getConnectionName());
  62. $this->assertSame('mohamed@laravel.com', $unSerialized->user->email);
  63. $serialized = serialize(new CollectionSerializationTestClass(ModelSerializationTestUser::on('testing')->get()));
  64. $unSerialized = unserialize($serialized);
  65. $this->assertSame('testing', $unSerialized->users[0]->getConnectionName());
  66. $this->assertSame('mohamed@laravel.com', $unSerialized->users[0]->email);
  67. $this->assertSame('testing', $unSerialized->users[1]->getConnectionName());
  68. $this->assertSame('taylor@laravel.com', $unSerialized->users[1]->email);
  69. }
  70. public function testItSerializeUserOnDifferentConnection()
  71. {
  72. $user = ModelSerializationTestUser::on('custom')->create([
  73. 'email' => 'mohamed@laravel.com',
  74. ]);
  75. ModelSerializationTestUser::on('custom')->create([
  76. 'email' => 'taylor@laravel.com',
  77. ]);
  78. $serialized = serialize(new ModelSerializationTestClass($user));
  79. $unSerialized = unserialize($serialized);
  80. $this->assertSame('custom', $unSerialized->user->getConnectionName());
  81. $this->assertSame('mohamed@laravel.com', $unSerialized->user->email);
  82. $serialized = serialize(new CollectionSerializationTestClass(ModelSerializationTestUser::on('custom')->get()));
  83. $unSerialized = unserialize($serialized);
  84. $this->assertSame('custom', $unSerialized->users[0]->getConnectionName());
  85. $this->assertSame('mohamed@laravel.com', $unSerialized->users[0]->email);
  86. $this->assertSame('custom', $unSerialized->users[1]->getConnectionName());
  87. $this->assertSame('taylor@laravel.com', $unSerialized->users[1]->email);
  88. }
  89. public function testItFailsIfModelsOnMultiConnections()
  90. {
  91. $this->expectException(LogicException::class);
  92. $this->expectExceptionMessage('Queueing collections with multiple model connections is not supported.');
  93. $user = ModelSerializationTestUser::on('custom')->create([
  94. 'email' => 'mohamed@laravel.com',
  95. ]);
  96. $user2 = ModelSerializationTestUser::create([
  97. 'email' => 'taylor@laravel.com',
  98. ]);
  99. $serialized = serialize(new CollectionSerializationTestClass(
  100. new Collection([$user, $user2])
  101. ));
  102. unserialize($serialized);
  103. }
  104. public function testItReloadsRelationships()
  105. {
  106. $order = tap(Order::create(), function (Order $order) {
  107. $order->wasRecentlyCreated = false;
  108. });
  109. $product1 = Product::create();
  110. $product2 = Product::create();
  111. Line::create(['order_id' => $order->id, 'product_id' => $product1->id]);
  112. Line::create(['order_id' => $order->id, 'product_id' => $product2->id]);
  113. $order->load('line', 'lines', 'products');
  114. $serialized = serialize(new ModelRelationSerializationTestClass($order));
  115. $unSerialized = unserialize($serialized);
  116. $this->assertEquals($unSerialized->order->getRelations(), $order->getRelations());
  117. }
  118. public function testItReloadsNestedRelationships()
  119. {
  120. $order = tap(Order::create(), function (Order $order) {
  121. $order->wasRecentlyCreated = false;
  122. });
  123. $product1 = Product::create();
  124. $product2 = Product::create();
  125. Line::create(['order_id' => $order->id, 'product_id' => $product1->id]);
  126. Line::create(['order_id' => $order->id, 'product_id' => $product2->id]);
  127. $order->load('line.product', 'lines', 'lines.product', 'products');
  128. $nestedSerialized = serialize(new ModelRelationSerializationTestClass($order));
  129. $nestedUnSerialized = unserialize($nestedSerialized);
  130. $this->assertEquals($nestedUnSerialized->order->getRelations(), $order->getRelations());
  131. }
  132. public function testItCanRunModelBootsAndTraitInitializations()
  133. {
  134. $model = new ModelBootTestWithTraitInitialization();
  135. $this->assertTrue($model->fooBar);
  136. $this->assertTrue($model::hasGlobalScope('foo_bar'));
  137. $model::clearBootedModels();
  138. $this->assertFalse($model::hasGlobalScope('foo_bar'));
  139. $unSerializedModel = unserialize(serialize($model));
  140. $this->assertFalse($unSerializedModel->fooBar);
  141. $this->assertTrue($model::hasGlobalScope('foo_bar'));
  142. }
  143. /**
  144. * Regression test for https://github.com/laravel/framework/issues/23068.
  145. */
  146. public function testItCanUnserializeNestedRelationshipsWithoutPivot()
  147. {
  148. $user = tap(User::create([
  149. 'email' => 'taylor@laravel.com',
  150. ]), function (User $user) {
  151. $user->wasRecentlyCreated = false;
  152. });
  153. $role1 = Role::create();
  154. $role2 = Role::create();
  155. RoleUser::create(['user_id' => $user->id, 'role_id' => $role1->id]);
  156. RoleUser::create(['user_id' => $user->id, 'role_id' => $role2->id]);
  157. $user->roles->each(function ($role) {
  158. $role->pivot->load('user', 'role');
  159. });
  160. $serialized = serialize(new ModelSerializationTestClass($user));
  161. unserialize($serialized);
  162. }
  163. public function testItSerializesAnEmptyCollection()
  164. {
  165. $serialized = serialize(new CollectionSerializationTestClass(
  166. new Collection([])
  167. ));
  168. unserialize($serialized);
  169. }
  170. public function testItSerializesACollectionInCorrectOrder()
  171. {
  172. ModelSerializationTestUser::create(['email' => 'mohamed@laravel.com']);
  173. ModelSerializationTestUser::create(['email' => 'taylor@laravel.com']);
  174. $serialized = serialize(new CollectionSerializationTestClass(
  175. ModelSerializationTestUser::orderByDesc('email')->get()
  176. ));
  177. $unserialized = unserialize($serialized);
  178. $this->assertSame('taylor@laravel.com', $unserialized->users->first()->email);
  179. $this->assertSame('mohamed@laravel.com', $unserialized->users->last()->email);
  180. }
  181. public function testItCanUnserializeACollectionInCorrectOrderAndHandleDeletedModels()
  182. {
  183. ModelSerializationTestUser::create(['email' => '2@laravel.com']);
  184. ModelSerializationTestUser::create(['email' => '3@laravel.com']);
  185. ModelSerializationTestUser::create(['email' => '1@laravel.com']);
  186. $serialized = serialize(new CollectionSerializationTestClass(
  187. ModelSerializationTestUser::orderByDesc('email')->get()
  188. ));
  189. ModelSerializationTestUser::where(['email' => '2@laravel.com'])->delete();
  190. $unserialized = unserialize($serialized);
  191. $this->assertCount(2, $unserialized->users);
  192. $this->assertSame('3@laravel.com', $unserialized->users->first()->email);
  193. $this->assertSame('1@laravel.com', $unserialized->users->last()->email);
  194. }
  195. public function testItCanUnserializeCustomCollection()
  196. {
  197. ModelSerializationTestCustomUser::create(['email' => 'mohamed@laravel.com']);
  198. ModelSerializationTestCustomUser::create(['email' => 'taylor@laravel.com']);
  199. $serialized = serialize(new CollectionSerializationTestClass(
  200. ModelSerializationTestCustomUser::all()
  201. ));
  202. $unserialized = unserialize($serialized);
  203. $this->assertInstanceOf(ModelSerializationTestCustomUserCollection::class, $unserialized->users);
  204. }
  205. /**
  206. * @requires PHP >= 7.4
  207. */
  208. public function testItSerializesTypedProperties()
  209. {
  210. require_once __DIR__.'/typed-properties.php';
  211. $user = ModelSerializationTestUser::create([
  212. 'email' => 'mohamed@laravel.com',
  213. ]);
  214. ModelSerializationTestUser::create([
  215. 'email' => 'taylor@laravel.com',
  216. ]);
  217. $serialized = serialize(new TypedPropertyTestClass($user, 5, ['James', 'Taylor', 'Mohamed']));
  218. $unSerialized = unserialize($serialized);
  219. $this->assertSame('testing', $unSerialized->user->getConnectionName());
  220. $this->assertSame('mohamed@laravel.com', $unSerialized->user->email);
  221. $this->assertSame(5, $unSerialized->getId());
  222. $this->assertSame(['James', 'Taylor', 'Mohamed'], $unSerialized->getNames());
  223. $serialized = serialize(new TypedPropertyCollectionTestClass(ModelSerializationTestUser::on('testing')->get()));
  224. $unSerialized = unserialize($serialized);
  225. $this->assertSame('testing', $unSerialized->users[0]->getConnectionName());
  226. $this->assertSame('mohamed@laravel.com', $unSerialized->users[0]->email);
  227. $this->assertSame('testing', $unSerialized->users[1]->getConnectionName());
  228. $this->assertSame('taylor@laravel.com', $unSerialized->users[1]->email);
  229. }
  230. public function test_model_serialization_structure()
  231. {
  232. $user = ModelSerializationTestUser::create([
  233. 'email' => 'taylor@laravel.com',
  234. ]);
  235. $serialized = serialize(new ModelSerializationParentAccessibleTestClass($user, $user, $user));
  236. $this->assertSame(
  237. 'O:78:"Illuminate\\Tests\\Integration\\Queue\\ModelSerializationParentAccessibleTestClass":2:{s:4:"user";O:45:"Illuminate\\Contracts\\Database\\ModelIdentifier":4:{s:5:"class";s:61:"Illuminate\\Tests\\Integration\\Queue\\ModelSerializationTestUser";s:2:"id";i:1;s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";}s:8:"'."\0".'*'."\0".'user2";O:45:"Illuminate\\Contracts\\Database\\ModelIdentifier":4:{s:5:"class";s:61:"Illuminate\\Tests\\Integration\\Queue\\ModelSerializationTestUser";s:2:"id";i:1;s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";}}', $serialized
  238. );
  239. }
  240. }
  241. trait TraitBootsAndInitializersTest
  242. {
  243. public $fooBar = false;
  244. public function initializeTraitBootsAndInitializersTest()
  245. {
  246. $this->fooBar = ! $this->fooBar;
  247. }
  248. public static function bootTraitBootsAndInitializersTest()
  249. {
  250. static::addGlobalScope('foo_bar', function () {
  251. });
  252. }
  253. }
  254. class ModelBootTestWithTraitInitialization extends Model
  255. {
  256. use TraitBootsAndInitializersTest;
  257. }
  258. class ModelSerializationTestUser extends Model
  259. {
  260. public $table = 'users';
  261. public $guarded = [];
  262. public $timestamps = false;
  263. }
  264. class ModelSerializationTestCustomUserCollection extends Collection
  265. {
  266. //
  267. }
  268. class ModelSerializationTestCustomUser extends Model
  269. {
  270. public $table = 'users';
  271. public $guarded = [];
  272. public $timestamps = false;
  273. public function newCollection(array $models = [])
  274. {
  275. return new ModelSerializationTestCustomUserCollection($models);
  276. }
  277. }
  278. class Order extends Model
  279. {
  280. public $guarded = [];
  281. public $timestamps = false;
  282. public function line()
  283. {
  284. return $this->hasOne(Line::class);
  285. }
  286. public function lines()
  287. {
  288. return $this->hasMany(Line::class);
  289. }
  290. public function products()
  291. {
  292. return $this->belongsToMany(Product::class, 'lines');
  293. }
  294. }
  295. class Line extends Model
  296. {
  297. public $guarded = [];
  298. public $timestamps = false;
  299. public function product()
  300. {
  301. return $this->belongsTo(Product::class);
  302. }
  303. }
  304. class Product extends Model
  305. {
  306. public $guarded = [];
  307. public $timestamps = false;
  308. }
  309. class User extends Model
  310. {
  311. public $guarded = [];
  312. public $timestamps = false;
  313. public function roles()
  314. {
  315. return $this->belongsToMany(Role::class)
  316. ->using(RoleUser::class);
  317. }
  318. }
  319. class Role extends Model
  320. {
  321. public $guarded = [];
  322. public $timestamps = false;
  323. public function users()
  324. {
  325. return $this->belongsToMany(User::class)
  326. ->using(RoleUser::class);
  327. }
  328. }
  329. class RoleUser extends Pivot
  330. {
  331. public $guarded = [];
  332. public $timestamps = false;
  333. public function user()
  334. {
  335. return $this->belongsTo(User::class);
  336. }
  337. public function role()
  338. {
  339. return $this->belongsTo(Role::class);
  340. }
  341. }
  342. class ModelSerializationTestClass
  343. {
  344. use SerializesModels;
  345. public $user;
  346. public function __construct($user)
  347. {
  348. $this->user = $user;
  349. }
  350. }
  351. class ModelSerializationAccessibleTestClass
  352. {
  353. use SerializesModels;
  354. public $user;
  355. protected $user2;
  356. private $user3;
  357. public function __construct($user, $user2, $user3)
  358. {
  359. $this->user = $user;
  360. $this->user2 = $user2;
  361. $this->user3 = $user3;
  362. }
  363. }
  364. class ModelSerializationParentAccessibleTestClass extends ModelSerializationAccessibleTestClass
  365. {
  366. //
  367. }
  368. class ModelRelationSerializationTestClass
  369. {
  370. use SerializesModels;
  371. public $order;
  372. public function __construct($order)
  373. {
  374. $this->order = $order;
  375. }
  376. }
  377. class CollectionSerializationTestClass
  378. {
  379. use SerializesModels;
  380. public $users;
  381. public function __construct($users)
  382. {
  383. $this->users = $users;
  384. }
  385. }