From df67f064690857d76ada15f98c528c4ac77a3ec3 Mon Sep 17 00:00:00 2001 From: Fred Bradley Date: Wed, 7 Apr 2021 10:10:14 +0100 Subject: [PATCH 01/31] Allow to retry jobs by queue name --- src/Illuminate/Queue/Console/RetryCommand.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Illuminate/Queue/Console/RetryCommand.php b/src/Illuminate/Queue/Console/RetryCommand.php index 2f651b60d098..13817c2b7487 100644 --- a/src/Illuminate/Queue/Console/RetryCommand.php +++ b/src/Illuminate/Queue/Console/RetryCommand.php @@ -18,6 +18,7 @@ class RetryCommand extends Command */ protected $signature = 'queue:retry {id?* : The ID of the failed job or "all" to retry all jobs} + {--queue= : Name of the queue to be retried} {--range=* : Range of job IDs (numeric) to be retried}'; /** @@ -62,6 +63,16 @@ protected function getJobIds() return Arr::pluck($this->laravel['queue.failer']->all(), 'id'); } + if ($queue = $this->option('queue')) { + $ids = collect($this->laravel['queue.failer']->all())->where('queue', $queue)->pluck('id')->toArray(); + + if (count($ids)===0) { + $this->error("Unable to find failed jobs in a queue named [{$queue}]."); + } + + return $ids; + } + if ($ranges = (array) $this->option('range')) { $ids = array_merge($ids, $this->getJobIdsByRanges($ranges)); } From be679d3c0e6da7ab161c61993feddf4eaeca4119 Mon Sep 17 00:00:00 2001 From: Fred Bradley Date: Wed, 7 Apr 2021 10:34:41 +0100 Subject: [PATCH 02/31] refactor: fix style spacing --- src/Illuminate/Queue/Console/RetryCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Queue/Console/RetryCommand.php b/src/Illuminate/Queue/Console/RetryCommand.php index 13817c2b7487..a1452dbf2c40 100644 --- a/src/Illuminate/Queue/Console/RetryCommand.php +++ b/src/Illuminate/Queue/Console/RetryCommand.php @@ -66,7 +66,7 @@ protected function getJobIds() if ($queue = $this->option('queue')) { $ids = collect($this->laravel['queue.failer']->all())->where('queue', $queue)->pluck('id')->toArray(); - if (count($ids)===0) { + if (count($ids) === 0) { $this->error("Unable to find failed jobs in a queue named [{$queue}]."); } From f2d9b595e51d564c5e1390eb42438c632e0daf36 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 Apr 2021 08:09:33 -0500 Subject: [PATCH 03/31] formatting --- src/Illuminate/Queue/Console/RetryCommand.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Queue/Console/RetryCommand.php b/src/Illuminate/Queue/Console/RetryCommand.php index a1452dbf2c40..f32caf49fd85 100644 --- a/src/Illuminate/Queue/Console/RetryCommand.php +++ b/src/Illuminate/Queue/Console/RetryCommand.php @@ -18,7 +18,7 @@ class RetryCommand extends Command */ protected $signature = 'queue:retry {id?* : The ID of the failed job or "all" to retry all jobs} - {--queue= : Name of the queue to be retried} + {--queue= : Retry all of the failed jobs for the specified queue} {--range=* : Range of job IDs (numeric) to be retried}'; /** @@ -64,10 +64,13 @@ protected function getJobIds() } if ($queue = $this->option('queue')) { - $ids = collect($this->laravel['queue.failer']->all())->where('queue', $queue)->pluck('id')->toArray(); + $ids = collect($this->laravel['queue.failer']->all()) + ->where('queue', $queue) + ->pluck('id') + ->toArray(); if (count($ids) === 0) { - $this->error("Unable to find failed jobs in a queue named [{$queue}]."); + $this->error("Unable to find failed jobs for queue [{$queue}]."); } return $ids; From c351a309f1a02098f9a7ee24a8a402e9ce06fead Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 7 Apr 2021 08:11:23 -0500 Subject: [PATCH 04/31] formatting --- src/Illuminate/Queue/Console/RetryCommand.php | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Illuminate/Queue/Console/RetryCommand.php b/src/Illuminate/Queue/Console/RetryCommand.php index f32caf49fd85..212883fecdcb 100644 --- a/src/Illuminate/Queue/Console/RetryCommand.php +++ b/src/Illuminate/Queue/Console/RetryCommand.php @@ -64,16 +64,7 @@ protected function getJobIds() } if ($queue = $this->option('queue')) { - $ids = collect($this->laravel['queue.failer']->all()) - ->where('queue', $queue) - ->pluck('id') - ->toArray(); - - if (count($ids) === 0) { - $this->error("Unable to find failed jobs for queue [{$queue}]."); - } - - return $ids; + return $this->getJobIdsByQueue($queue); } if ($ranges = (array) $this->option('range')) { @@ -83,6 +74,26 @@ protected function getJobIds() return array_values(array_filter(array_unique($ids))); } + /** + * Get the job IDs by queue, if applicable. + * + * @param string $queue + * @return array + */ + protected function getJobIdsByQueue($queue) + { + $ids = collect($this->laravel['queue.failer']->all()) + ->where('queue', $queue) + ->pluck('id') + ->toArray(); + + if (count($ids) === 0) { + $this->error("Unable to find failed jobs for queue [{$queue}]."); + } + + return $ids; + } + /** * Get the job IDs ranges, if applicable. * From 42102589bc7f7b8533ee1b815ef0cc18017d4e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rihards=20=C5=A0=C4=8Deredins?= Date: Thu, 8 Apr 2021 15:38:18 +0300 Subject: [PATCH 05/31] Add more messages for detecting lost connection (happens during managed PostgreSQL upgrade on DigitalOcean) (#36911) --- src/Illuminate/Database/DetectsLostConnections.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Illuminate/Database/DetectsLostConnections.php b/src/Illuminate/Database/DetectsLostConnections.php index 191eefedc891..93be53b2fdc9 100644 --- a/src/Illuminate/Database/DetectsLostConnections.php +++ b/src/Illuminate/Database/DetectsLostConnections.php @@ -52,6 +52,8 @@ protected function causedByLostConnection(Throwable $e) 'Temporary failure in name resolution', 'SSL: Broken pipe', 'SQLSTATE[08S01]: Communication link failure', + 'SQLSTATE[08006] [7] could not connect to server: Connection refused Is the server running on host', + 'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: No route to host', ]); } } From 5e0ef03e9fd664a31d0150df080df8e6549e2ef8 Mon Sep 17 00:00:00 2001 From: Lachlan Krautz Date: Thu, 8 Apr 2021 22:42:49 +1000 Subject: [PATCH 06/31] [8.x] Model::delete throw LogicException not Exception (#36914) * Throw LogicException instead of Exception on delete model with missing primary key * Update Model.php Co-authored-by: Taylor Otwell --- src/Illuminate/Database/Eloquent/Model.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 62fcd058e258..245a103bce40 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -3,7 +3,6 @@ namespace Illuminate\Database\Eloquent; use ArrayAccess; -use Exception; use Illuminate\Contracts\Queue\QueueableCollection; use Illuminate\Contracts\Queue\QueueableEntity; use Illuminate\Contracts\Routing\UrlRoutable; @@ -20,6 +19,7 @@ use Illuminate\Support\Str; use Illuminate\Support\Traits\ForwardsCalls; use JsonSerializable; +use LogicException; abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable { @@ -1097,14 +1097,14 @@ public static function destroy($ids) * * @return bool|null * - * @throws \Exception + * @throws \LogicException */ public function delete() { $this->mergeAttributesFromClassCasts(); if (is_null($this->getKeyName())) { - throw new Exception('No primary key defined on model.'); + throw new LogicException('No primary key defined on model.'); } // If the model doesn't exist, there is nothing to delete so we'll just return From 94fd9662033b9b7554cb3ba96fbafcc44fc4f7b4 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Thu, 8 Apr 2021 21:58:09 +0200 Subject: [PATCH 07/31] Allow testing of Blade components that return closures (#36919) --- .../Foundation/Testing/Concerns/InteractsWithViews.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithViews.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithViews.php index 574009a68f95..faa6c64a367e 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithViews.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithViews.php @@ -2,6 +2,7 @@ namespace Illuminate\Foundation\Testing\Concerns; +use Closure; use Illuminate\Support\Facades\View as ViewFacade; use Illuminate\Support\MessageBag; use Illuminate\Support\Str; @@ -58,6 +59,10 @@ protected function component(string $componentClass, array $data = []) $view = $component->resolveView(); + if ($view instanceof Closure) { + $view = $view($data); + } + return $view instanceof View ? new TestView($view->with($component->data())) : new TestView(view($view, $component->data())); From a4084639eea11ac2649325f4cd9a7c267d01a878 Mon Sep 17 00:00:00 2001 From: Theraloss Date: Fri, 9 Apr 2021 14:45:47 +0200 Subject: [PATCH 08/31] add eloquent builder to param type of selectSub (#36926) --- src/Illuminate/Database/Query/Builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 710d0a3a568c..befc8a8cfa33 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -244,7 +244,7 @@ public function select($columns = ['*']) /** * Add a subselect expression to the query. * - * @param \Closure|\Illuminate\Database\Query\Builder|string $query + * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query * @param string $as * @return $this * From bf3d5e319f0bd928a493222adb1acd9d23c1db68 Mon Sep 17 00:00:00 2001 From: Propaganistas Date: Fri, 9 Apr 2021 14:48:38 +0200 Subject: [PATCH 09/31] [8.x] Fix clone() on EloquentBuilder (#36924) * Support clone() on EloquentBuilder * Update Builder.php Co-authored-by: Taylor Otwell --- src/Illuminate/Database/Eloquent/Builder.php | 10 ++++++++++ tests/Database/DatabaseEloquentBuilderTest.php | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index ca84d06cdbc1..a8f2fb87842c 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -1608,6 +1608,16 @@ protected static function registerMixin($mixin, $replace) } } + /** + * Clone the Eloquent query builder. + * + * @return static + */ + public function clone() + { + return clone $this; + } + /** * Force a clone of the underlying query builder when cloning. * diff --git a/tests/Database/DatabaseEloquentBuilderTest.php b/tests/Database/DatabaseEloquentBuilderTest.php index 0809f8e0fe98..682545ff9b98 100755 --- a/tests/Database/DatabaseEloquentBuilderTest.php +++ b/tests/Database/DatabaseEloquentBuilderTest.php @@ -1497,6 +1497,18 @@ public function testWithCastsMethod() $builder->withCasts(['foo' => 'bar']); } + public function testClone() + { + $query = new BaseBuilder(m::mock(ConnectionInterface::class), new Grammar, m::mock(Processor::class)); + $builder = new Builder($query); + $builder->select('*')->from('users'); + $clone = $builder->clone()->where('email', 'foo'); + + $this->assertNotSame($builder, $clone); + $this->assertSame('select * from "users"', $builder->toSql()); + $this->assertSame('select * from "users" where "email" = ?', $clone->toSql()); + } + protected function mockConnectionForModel($model, $database) { $grammarClass = 'Illuminate\Database\Query\Grammars\\'.$database.'Grammar'; From 9311dafa61fe5e7fd1aaffa525eca3dd15992fef Mon Sep 17 00:00:00 2001 From: Thiago Barcala Date: Wed, 7 Apr 2021 21:27:52 +0200 Subject: [PATCH 10/31] Implement anonymous migrations --- .../Database/Migrations/Migrator.php | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/Illuminate/Database/Migrations/Migrator.php b/src/Illuminate/Database/Migrations/Migrator.php index 093e41e78a43..b9bf18195a54 100755 --- a/src/Illuminate/Database/Migrations/Migrator.php +++ b/src/Illuminate/Database/Migrations/Migrator.php @@ -13,6 +13,7 @@ use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Str; +use ReflectionClass; use Symfony\Component\Console\Output\OutputInterface; class Migrator @@ -185,9 +186,8 @@ protected function runUp($file, $batch, $pretend) // First we will resolve a "real" instance of the migration class from this // migration file name. Once we have the instances we can run the actual // command such as "up" or "down", or we can just simulate the action. - $migration = $this->resolve( - $name = $this->getMigrationName($file) - ); + $migration = $this->resolvePath($file); + $name = $this->getMigrationName($file); if ($pretend) { return $this->pretendToRun($migration, 'up'); @@ -348,9 +348,8 @@ protected function runDown($file, $migration, $pretend) // First we will get the file name of the migration so we can resolve out an // instance of the migration. Once we get an instance we can either run a // pretend execution of the migration or we can run the real migration. - $instance = $this->resolve( - $name = $this->getMigrationName($file) - ); + $instance = $this->resolvePath($file); + $name = $this->getMigrationName($file); $this->note("Rolling back: {$name}"); @@ -411,7 +410,7 @@ protected function runMigration($migration, $method) protected function pretendToRun($migration, $method) { foreach ($this->getQueries($migration, $method) as $query) { - $name = get_class($migration); + $name = $this->getMigrationName((new ReflectionClass($migration))->getFileName()); $this->note("{$name}: {$query['query']}"); } @@ -448,11 +447,38 @@ protected function getQueries($migration, $method) */ public function resolve($file) { - $class = Str::studly(implode('_', array_slice(explode('_', $file), 4))); + $class = $this->getMigrationClass($file); return new $class; } + /** + * Resolve a migration instance from migration path. + * + * @param string $path + * @return object + */ + protected function resolvePath(string $path) + { + $class = $this->getMigrationClass($this->getMigrationName($path)); + if (class_exists($class)) { + return new $class; + } + + return $this->files->getRequire($path); + } + + /** + * Generate migration class name based on migration name. + * + * @param string $migrationName + * @return string + */ + protected function getMigrationClass(string $migrationName): string + { + return Str::studly(implode('_', array_slice(explode('_', $migrationName), 4))); + } + /** * Get all of the migration files in a given path. * From 9cdb5cc62a0a6c5c86464c62b0b1360f220a7994 Mon Sep 17 00:00:00 2001 From: Thiago Barcala Date: Wed, 7 Apr 2021 21:22:35 +0200 Subject: [PATCH 11/31] Extend MigratorTest --- tests/Integration/Migration/MigratorTest.php | 74 +++++++++++++++---- .../2015_10_04_000000_modify_people_table.php | 31 ++++++++ .../2016_10_04_000000_modify_people_table.php | 31 ++++++++ 3 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 tests/Integration/Migration/fixtures/2015_10_04_000000_modify_people_table.php create mode 100644 tests/Integration/Migration/fixtures/2016_10_04_000000_modify_people_table.php diff --git a/tests/Integration/Migration/MigratorTest.php b/tests/Integration/Migration/MigratorTest.php index 50ee6f3cbd6b..9e685379bb2a 100644 --- a/tests/Integration/Migration/MigratorTest.php +++ b/tests/Integration/Migration/MigratorTest.php @@ -2,11 +2,29 @@ namespace Illuminate\Tests\Integration\Migration; +use Illuminate\Support\Facades\DB; +use Mockery; +use Mockery\Mock; use Orchestra\Testbench\TestCase; -use PDOException; +use Symfony\Component\Console\Output\OutputInterface; class MigratorTest extends TestCase { + /** + * @var Mock + */ + private $output; + + protected function setUp(): void + { + parent::setUp(); + + $this->output = Mockery::mock(OutputInterface::class); + $this->subject = $this->app->make('migrator'); + $this->subject->setOutput($this->output); + $this->subject->getRepository()->createRepository(); + } + protected function getEnvironmentSetUp($app) { $app['config']->set('app.debug', 'true'); @@ -19,25 +37,55 @@ protected function getEnvironmentSetUp($app) ]); } - public function testDontDisplayOutputWhenOutputObjectIsNotAvailable() + public function testMigrate() + { + $this->expectOutput('Migrating: 2014_10_12_000000_create_people_table'); + $this->expectOutput(Mockery::pattern('#Migrated: 2014_10_12_000000_create_people_table (.*)#')); + $this->expectOutput('Migrating: 2015_10_04_000000_modify_people_table'); + $this->expectOutput(Mockery::pattern('#Migrated: 2015_10_04_000000_modify_people_table (.*)#')); + $this->expectOutput('Migrating: 2016_10_04_000000_modify_people_table'); + $this->expectOutput(Mockery::pattern('#Migrated: 2016_10_04_000000_modify_people_table (.*)#')); + + $this->subject->run([__DIR__.'/fixtures']); + + self::assertTrue(DB::getSchemaBuilder()->hasTable('people')); + self::assertTrue(DB::getSchemaBuilder()->hasColumn('people', 'first_name')); + self::assertTrue(DB::getSchemaBuilder()->hasColumn('people', 'last_name')); + } + + public function testRollback() { - $migrator = $this->app->make('migrator'); + $this->getConnection()->statement('CREATE TABLE people(id INT, first_name VARCHAR, last_name VARCHAR);'); + $this->subject->getRepository()->log('2014_10_12_000000_create_people_table', 1); + $this->subject->getRepository()->log('2015_10_04_000000_modify_people_table', 1); + $this->subject->getRepository()->log('2016_10_04_000000_modify_people_table', 1); - $migrator->getRepository()->createRepository(); + $this->expectOutput('Rolling back: 2016_10_04_000000_modify_people_table'); + $this->expectOutput(Mockery::pattern('#Rolled back: 2016_10_04_000000_modify_people_table (.*)#')); + $this->expectOutput('Rolling back: 2015_10_04_000000_modify_people_table'); + $this->expectOutput(Mockery::pattern('#Rolled back: 2015_10_04_000000_modify_people_table (.*)#')); + $this->expectOutput('Rolling back: 2014_10_12_000000_create_people_table'); + $this->expectOutput(Mockery::pattern('#Rolled back: 2014_10_12_000000_create_people_table (.*)#')); - $migrator->run([__DIR__.'/fixtures']); + $this->subject->rollback([__DIR__.'/fixtures']); - $this->assertTrue($this->tableExists('people')); + self::assertFalse(DB::getSchemaBuilder()->hasTable('people')); } - private function tableExists($table): bool + public function testPretendMigrate() { - try { - $this->app->make('db')->select("SELECT COUNT(*) FROM $table"); - } catch (PDOException $e) { - return false; - } + $this->expectOutput('2014_10_12_000000_create_people_table: create table "people" ("id" integer not null primary key autoincrement, "name" varchar not null, "email" varchar not null, "password" varchar not null, "remember_token" varchar, "created_at" datetime, "updated_at" datetime)'); + $this->expectOutput('2014_10_12_000000_create_people_table: create unique index "people_email_unique" on "people" ("email")'); + $this->expectOutput('2015_10_04_000000_modify_people_table: alter table "people" add column "first_name" varchar'); + $this->expectOutput('2016_10_04_000000_modify_people_table: alter table "people" add column "last_name" varchar'); + + $this->subject->run([__DIR__.'/fixtures'], ['pretend' => true]); - return true; + self::assertFalse(DB::getSchemaBuilder()->hasTable('people')); + } + + private function expectOutput($argument): void + { + $this->output->shouldReceive('writeln')->once()->with($argument); } } diff --git a/tests/Integration/Migration/fixtures/2015_10_04_000000_modify_people_table.php b/tests/Integration/Migration/fixtures/2015_10_04_000000_modify_people_table.php new file mode 100644 index 000000000000..88ac706cd12a --- /dev/null +++ b/tests/Integration/Migration/fixtures/2015_10_04_000000_modify_people_table.php @@ -0,0 +1,31 @@ +string('first_name')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('people', function (Blueprint $table) { + $table->dropColumn('first_name'); + }); + } +}; diff --git a/tests/Integration/Migration/fixtures/2016_10_04_000000_modify_people_table.php b/tests/Integration/Migration/fixtures/2016_10_04_000000_modify_people_table.php new file mode 100644 index 000000000000..6492b6d7f55a --- /dev/null +++ b/tests/Integration/Migration/fixtures/2016_10_04_000000_modify_people_table.php @@ -0,0 +1,31 @@ +string('last_name')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('people', function (Blueprint $table) { + $table->dropColumn('last_name'); + }); + } +}; From 64272b49e9426eec8526a097471bcb779e3ec001 Mon Sep 17 00:00:00 2001 From: Thiago Barcala Date: Sun, 11 Apr 2021 08:38:40 +0200 Subject: [PATCH 12/31] Display migration class name in pretendToRun Due to backward compatibility, classic migrations (non-anonymous) should have their class name displayed while running migrations with --pretend, instead of the migration name, like anywhere else in the code. --- src/Illuminate/Database/Migrations/Migrator.php | 7 ++++++- tests/Integration/Migration/MigratorTest.php | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Migrations/Migrator.php b/src/Illuminate/Database/Migrations/Migrator.php index b9bf18195a54..b51db2a142c5 100755 --- a/src/Illuminate/Database/Migrations/Migrator.php +++ b/src/Illuminate/Database/Migrations/Migrator.php @@ -410,7 +410,12 @@ protected function runMigration($migration, $method) protected function pretendToRun($migration, $method) { foreach ($this->getQueries($migration, $method) as $query) { - $name = $this->getMigrationName((new ReflectionClass($migration))->getFileName()); + $name = get_class($migration); + + $reflectionClass = new ReflectionClass($migration); + if ($reflectionClass->isAnonymous()) { + $name = $this->getMigrationName($reflectionClass->getFileName()); + } $this->note("{$name}: {$query['query']}"); } diff --git a/tests/Integration/Migration/MigratorTest.php b/tests/Integration/Migration/MigratorTest.php index 9e685379bb2a..08dd9c97862f 100644 --- a/tests/Integration/Migration/MigratorTest.php +++ b/tests/Integration/Migration/MigratorTest.php @@ -74,8 +74,8 @@ public function testRollback() public function testPretendMigrate() { - $this->expectOutput('2014_10_12_000000_create_people_table: create table "people" ("id" integer not null primary key autoincrement, "name" varchar not null, "email" varchar not null, "password" varchar not null, "remember_token" varchar, "created_at" datetime, "updated_at" datetime)'); - $this->expectOutput('2014_10_12_000000_create_people_table: create unique index "people_email_unique" on "people" ("email")'); + $this->expectOutput('CreatePeopleTable: create table "people" ("id" integer not null primary key autoincrement, "name" varchar not null, "email" varchar not null, "password" varchar not null, "remember_token" varchar, "created_at" datetime, "updated_at" datetime)'); + $this->expectOutput('CreatePeopleTable: create unique index "people_email_unique" on "people" ("email")'); $this->expectOutput('2015_10_04_000000_modify_people_table: alter table "people" add column "first_name" varchar'); $this->expectOutput('2016_10_04_000000_modify_people_table: alter table "people" add column "last_name" varchar'); From ef99cffd4938759cecfe2cb19888e9ff3e942b87 Mon Sep 17 00:00:00 2001 From: Felix Schmid Date: Sun, 11 Apr 2021 18:12:42 +0200 Subject: [PATCH 13/31] [8.x] Add more readable `missing` method to Session\Store (#36937) * Add missing helper for session * Update Store.php Co-authored-by: Taylor Otwell --- src/Illuminate/Session/Store.php | 11 +++++++++++ tests/Session/SessionStoreTest.php | 16 ++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/Illuminate/Session/Store.php b/src/Illuminate/Session/Store.php index ff2a3b966a20..151e8b6330b5 100755 --- a/src/Illuminate/Session/Store.php +++ b/src/Illuminate/Session/Store.php @@ -193,6 +193,17 @@ public function exists($key) }); } + /** + * Determine if the given key is missing from the session data. + * + * @param string|array $key + * @return bool + */ + public function missing($key) + { + return ! $this->exists($key); + } + /** * Checks if a key is present and not null. * diff --git a/tests/Session/SessionStoreTest.php b/tests/Session/SessionStoreTest.php index 5871cc657a85..188bdce4d89f 100644 --- a/tests/Session/SessionStoreTest.php +++ b/tests/Session/SessionStoreTest.php @@ -452,6 +452,22 @@ public function testKeyExists() $this->assertFalse($session->exists(['hulk.two'])); } + public function testKeyMissing() + { + $session = $this->getSession(); + $session->put('foo', 'bar'); + $this->assertFalse($session->missing('foo')); + $session->put('baz', null); + $session->put('hulk', ['one' => true]); + $this->assertFalse($session->has('baz')); + $this->assertFalse($session->missing('baz')); + $this->assertTrue($session->missing('bogus')); + $this->assertFalse($session->missing(['foo', 'baz'])); + $this->assertTrue($session->missing(['foo', 'baz', 'bogus'])); + $this->assertFalse($session->missing(['hulk.one'])); + $this->assertTrue($session->missing(['hulk.two'])); + } + public function testRememberMethodCallsPutAndReturnsDefault() { $session = $this->getSession(); From 8f2d3ef57d4d13de850181e5a253b14923893909 Mon Sep 17 00:00:00 2001 From: Andrea Marco Sartori Date: Mon, 12 Apr 2021 09:23:29 +1000 Subject: [PATCH 14/31] Replace deprecated functions --- src/Illuminate/Http/Client/Factory.php | 6 ++++-- src/Illuminate/Http/Client/RequestException.php | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Http/Client/Factory.php b/src/Illuminate/Http/Client/Factory.php index 4db3e1fa98a0..83fd71fd0ae0 100644 --- a/src/Illuminate/Http/Client/Factory.php +++ b/src/Illuminate/Http/Client/Factory.php @@ -3,7 +3,7 @@ namespace Illuminate\Http\Client; use Closure; -use function GuzzleHttp\Promise\promise_for; +use GuzzleHttp\Promise\Create; use GuzzleHttp\Psr7\Response as Psr7Response; use Illuminate\Support\Str; use Illuminate\Support\Traits\Macroable; @@ -34,6 +34,8 @@ * @method \Illuminate\Http\Client\PendingRequest withoutVerifying() * @method \Illuminate\Http\Client\PendingRequest dump() * @method \Illuminate\Http\Client\PendingRequest dd() + * @method \Illuminate\Http\Client\PendingRequest async() + * @method \Illuminate\Http\Client\Pool pool() * @method \Illuminate\Http\Client\Response delete(string $url, array $data = []) * @method \Illuminate\Http\Client\Response get(string $url, array $query = []) * @method \Illuminate\Http\Client\Response head(string $url, array $query = []) @@ -104,7 +106,7 @@ public static function response($body = null, $status = 200, $headers = []) $headers['Content-Type'] = 'application/json'; } - return promise_for(new Psr7Response($status, $headers, $body)); + return Create::promiseFor(new Psr7Response($status, $headers, $body)); } /** diff --git a/src/Illuminate/Http/Client/RequestException.php b/src/Illuminate/Http/Client/RequestException.php index fe2ea8be4252..5dd37e306ebc 100644 --- a/src/Illuminate/Http/Client/RequestException.php +++ b/src/Illuminate/Http/Client/RequestException.php @@ -2,6 +2,8 @@ namespace Illuminate\Http\Client; +use GuzzleHttp\Psr7\Message; + class RequestException extends HttpClientException { /** @@ -34,7 +36,7 @@ protected function prepareMessage(Response $response) { $message = "HTTP request returned status code {$response->status()}"; - $summary = \GuzzleHttp\Psr7\get_message_body_summary($response->toPsrResponse()); + $summary = Message::bodySummary($response->toPsrResponse()); return is_null($summary) ? $message : $message .= ":\n{$summary}\n"; } From f30af7fd98df98b843e9fc21525c4380901eb4a4 Mon Sep 17 00:00:00 2001 From: Andrea Marco Sartori Date: Mon, 12 Apr 2021 09:25:34 +1000 Subject: [PATCH 15/31] Enable concurrency of asynchronous requests --- src/Illuminate/Http/Client/PendingRequest.php | 157 ++++++++++++++++-- src/Illuminate/Http/Client/Pool.php | 84 ++++++++++ 2 files changed, 230 insertions(+), 11 deletions(-) create mode 100644 src/Illuminate/Http/Client/Pool.php diff --git a/src/Illuminate/Http/Client/PendingRequest.php b/src/Illuminate/Http/Client/PendingRequest.php index 5801bc0b70f0..79be9399c874 100644 --- a/src/Illuminate/Http/Client/PendingRequest.php +++ b/src/Illuminate/Http/Client/PendingRequest.php @@ -5,10 +5,13 @@ use GuzzleHttp\Client; use GuzzleHttp\Cookie\CookieJar; use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\TransferException; use GuzzleHttp\HandlerStack; use Illuminate\Support\Collection; use Illuminate\Support\Str; use Illuminate\Support\Traits\Macroable; +use Psr\Http\Message\MessageInterface; use Symfony\Component\VarDumper\VarDumper; class PendingRequest @@ -22,6 +25,13 @@ class PendingRequest */ protected $factory; + /** + * The client instance. + * + * @var \GuzzleHttp\Client + */ + protected $client; + /** * The base URL for the request. * @@ -106,6 +116,20 @@ class PendingRequest */ protected $middleware; + /** + * Whether the requests should be asynchronous. + * + * @var bool + */ + protected $async = false; + + /** + * The pending request promise. + * + * @var \GuzzleHttp\Promise\PromiseInterface + */ + protected $promise; + /** * Create a new HTTP Client instance. * @@ -601,18 +625,14 @@ public function send(string $method, string $url, array $options = []) [$this->pendingBody, $this->pendingFiles] = [null, []]; + if ($this->async) { + return $this->makePromise($method, $url, $options); + } + return retry($this->tries ?? 1, function () use ($method, $url, $options) { try { - $laravelData = $this->parseRequestData($method, $url, $options); - - return tap(new Response($this->buildClient()->request($method, $url, $this->mergeOptions([ - 'laravel_data' => $laravelData, - 'on_stats' => function ($transferStats) { - $this->transferStats = $transferStats; - }, - ], $options))), function ($response) { - $response->cookies = $this->cookies; - $response->transferStats = $this->transferStats; + return tap(new Response($this->sendRequest($method, $url, $options)), function ($response) { + $this->populateResponse($response); if ($this->tries > 1 && ! $response->successful()) { $response->throw(); @@ -637,6 +657,49 @@ protected function parseMultipartBodyFormat(array $data) })->values()->all(); } + /** + * Send an asynchronous request to the given URL. + * + * @param string $method + * @param string $url + * @param array $options + * @return \GuzzleHttp\Promise\PromiseInterface + */ + protected function makePromise(string $method, string $url, array $options = []) + { + return $this->promise = $this->sendRequest($method, $url, $options) + ->then(function (MessageInterface $message) { + return $this->populateResponse(new Response($message)); + }) + ->otherwise(function (TransferException $e) { + return $e instanceof RequestException ? $this->populateResponse(new Response($e->getResponse())) : $e; + }); + } + + /** + * Send a request either synchronously or asynchronously. + * + * @param string $method + * @param string $url + * @param array $options + * @return \Psr\Http\Message\MessageInterface|\GuzzleHttp\Promise\PromiseInterface + * + * @throws \Exception + */ + protected function sendRequest(string $method, string $url, array $options = []) + { + $clientMethod = $this->async ? 'requestAsync' : 'request'; + + $laravelData = $this->parseRequestData($method, $url, $options); + + return $this->buildClient()->$clientMethod($method, $url, $this->mergeOptions([ + 'laravel_data' => $laravelData, + 'on_stats' => function ($transferStats) { + $this->transferStats = $transferStats; + }, + ], $options)); + } + /** * Get the request data as an array so that we can attach it to the request for convenient assertions. * @@ -664,6 +727,34 @@ protected function parseRequestData($method, $url, array $options) return $laravelData; } + /** + * Populate the given response with additional data. + * + * @param \Illuminate\Http\Client\Response $response + * @return \Illuminate\Http\Client\Response + */ + protected function populateResponse(Response $response) + { + $response->cookies = $this->cookies; + + $response->transferStats = $this->transferStats; + + return $response; + } + + /** + * Set the client instance. + * + * @param \GuzzleHttp\Client $client + * @return $this + */ + public function setClient(Client $client) + { + $this->client = $client; + + return $this; + } + /** * Build the Guzzle client. * @@ -671,7 +762,7 @@ protected function parseRequestData($method, $url, array $options) */ public function buildClient() { - return new Client([ + return $this->client = $this->client ?: new Client([ 'handler' => $this->buildHandlerStack(), 'cookies' => true, ]); @@ -826,4 +917,48 @@ public function stub($callback) return $this; } + + /** + * Toggle asynchronicity in requests. + * + * @param bool $async + * @return $this + */ + public function async(bool $async = true) + { + $this->async = $async; + + return $this; + } + + /** + * Send a pool of asynchronous requests concurrently + * + * @param callable $callback + * @return array + */ + public function pool(callable $callback) + { + $results = []; + + $requests = tap(new Pool($this->factory), $callback)->getRequests(); + + foreach ($requests as $key => $item) { + $results[$key] = $item instanceof static ? $item->getPromise()->wait() : $item->wait(); + } + + ksort($results); + + return $results; + } + + /** + * Retrieve the pending request promise. + * + * @return \GuzzleHttp\Promise\PromiseInterface|null + */ + public function getPromise() + { + return $this->promise; + } } diff --git a/src/Illuminate/Http/Client/Pool.php b/src/Illuminate/Http/Client/Pool.php new file mode 100644 index 000000000000..8b04f4300155 --- /dev/null +++ b/src/Illuminate/Http/Client/Pool.php @@ -0,0 +1,84 @@ +factory = $factory ?: new Factory(); + + $this->client = $this->factory->buildClient(); + } + + /** + * Add a request to the pool with a key. + * + * @param string $key + * @return \Illuminate\Http\Client\PendingRequest + */ + public function add(string $key) + { + return $this->pool[$key] = $this->asyncRequest(); + } + + /** + * Retrieve a new async pending request. + * + * @return \Illuminate\Http\Client\PendingRequest + */ + protected function asyncRequest() + { + // the same client instance needs to be shared across all async requests + return $this->factory->setClient($this->client)->async(); + } + + /** + * Retrieve the requests in the pool. + * + * @return array + */ + public function getRequests() + { + return $this->pool; + } + + /** + * Add a request to the pool with a numeric index. + * + * @param string $method + * @param array $parameters + * @return \Illuminate\Http\Client\PendingRequest + */ + public function __call($method, $parameters) + { + return $this->pool[] = $this->asyncRequest()->$method(...$parameters); + } +} From 529cf647a2e97b882661897accc2fdfaa3f568c0 Mon Sep 17 00:00:00 2001 From: Andrea Marco Sartori Date: Mon, 12 Apr 2021 09:26:41 +1000 Subject: [PATCH 16/31] Add methods to make requests async and send a pool of requests --- src/Illuminate/Support/Facades/Http.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Illuminate/Support/Facades/Http.php b/src/Illuminate/Support/Facades/Http.php index 426d574789c5..51763b6caef0 100644 --- a/src/Illuminate/Support/Facades/Http.php +++ b/src/Illuminate/Support/Facades/Http.php @@ -32,6 +32,8 @@ * @method static \Illuminate\Http\Client\PendingRequest withoutVerifying() * @method static \Illuminate\Http\Client\PendingRequest dump() * @method static \Illuminate\Http\Client\PendingRequest dd() + * @method static \Illuminate\Http\Client\PendingRequest async() + * @method static \Illuminate\Http\Client\Pool pool() * @method static \Illuminate\Http\Client\Response delete(string $url, array $data = []) * @method static \Illuminate\Http\Client\Response get(string $url, array $query = []) * @method static \Illuminate\Http\Client\Response head(string $url, array $query = []) From 4cb083aea57e7c620f2d87feb3b259c55f1d9da4 Mon Sep 17 00:00:00 2001 From: Andrea Marco Sartori Date: Mon, 12 Apr 2021 09:27:00 +1000 Subject: [PATCH 17/31] Create tests --- tests/Http/HttpClientTest.php | 69 +++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/Http/HttpClientTest.php b/tests/Http/HttpClientTest.php index 12d7a1806c21..473a5b4e06c5 100644 --- a/tests/Http/HttpClientTest.php +++ b/tests/Http/HttpClientTest.php @@ -2,8 +2,11 @@ namespace Illuminate\Tests\Http; +use GuzzleHttp\Promise\PromiseInterface; use GuzzleHttp\Psr7\Response as Psr7Response; use Illuminate\Http\Client\Factory; +use Illuminate\Http\Client\PendingRequest; +use Illuminate\Http\Client\Pool; use Illuminate\Http\Client\Request; use Illuminate\Http\Client\RequestException; use Illuminate\Http\Client\Response; @@ -833,4 +836,70 @@ public function testResponseSequenceIsMacroable() $this->assertSame('yes!', $this->factory->fakeSequence()->customMethod()); } + + public function testRequestsCanBeAsync() + { + $request = new PendingRequest($this->factory); + + $promise = $request->async()->get('http://foo.com'); + + $this->assertInstanceOf(PromiseInterface::class, $promise); + + $this->assertSame($promise, $request->getPromise()); + } + + public function testClientCanBeSet() + { + $client = $this->factory->buildClient(); + + $request = new PendingRequest($this->factory); + + $this->assertNotSame($client, $request->buildClient()); + + $request->setClient($client); + + $this->assertSame($client, $request->buildClient()); + } + + public function testMultipleRequestsAreSentInThePool() + { + $this->factory->fake([ + '200.com' => $this->factory::response('', 200), + '400.com' => $this->factory::response('', 400), + '500.com' => $this->factory::response('', 500), + ]); + + $responses = $this->factory->pool(function (Pool $pool) { + return [ + $pool->get('200.com'), + $pool->get('400.com'), + $pool->get('500.com'), + ]; + }); + + $this->assertSame(200, $responses[0]->status()); + $this->assertSame(400, $responses[1]->status()); + $this->assertSame(500, $responses[2]->status()); + } + + public function testMultipleRequestsAreSentInThePoolWithKeys() + { + $this->factory->fake([ + '200.com' => $this->factory::response('', 200), + '400.com' => $this->factory::response('', 400), + '500.com' => $this->factory::response('', 500), + ]); + + $responses = $this->factory->pool(function (Pool $pool) { + return [ + $pool->add('test200')->get('200.com'), + $pool->add('test400')->get('400.com'), + $pool->add('test500')->get('500.com'), + ]; + }); + + $this->assertSame(200, $responses['test200']->status()); + $this->assertSame(400, $responses['test400']->status()); + $this->assertSame(500, $responses['test500']->status()); + } } From 0b4a04e77dc1198f0d64702a0e93ce203a680d37 Mon Sep 17 00:00:00 2001 From: Andrea Marco Sartori Date: Mon, 12 Apr 2021 09:37:09 +1000 Subject: [PATCH 18/31] Revert deprecated function to support PHP 7.3 --- src/Illuminate/Http/Client/Factory.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Http/Client/Factory.php b/src/Illuminate/Http/Client/Factory.php index 83fd71fd0ae0..f9a507bf90a3 100644 --- a/src/Illuminate/Http/Client/Factory.php +++ b/src/Illuminate/Http/Client/Factory.php @@ -3,12 +3,13 @@ namespace Illuminate\Http\Client; use Closure; -use GuzzleHttp\Promise\Create; use GuzzleHttp\Psr7\Response as Psr7Response; use Illuminate\Support\Str; use Illuminate\Support\Traits\Macroable; use PHPUnit\Framework\Assert as PHPUnit; +use function GuzzleHttp\Promise\promise_for; + /** * @method \Illuminate\Http\Client\PendingRequest accept(string $contentType) * @method \Illuminate\Http\Client\PendingRequest acceptJson() @@ -106,7 +107,7 @@ public static function response($body = null, $status = 200, $headers = []) $headers['Content-Type'] = 'application/json'; } - return Create::promiseFor(new Psr7Response($status, $headers, $body)); + return promise_for(new Psr7Response($status, $headers, $body)); } /** From 97bfb028613c9d46dca3ab8ddd4c43303d51d4e9 Mon Sep 17 00:00:00 2001 From: Andrea Marco Sartori Date: Mon, 12 Apr 2021 09:50:10 +1000 Subject: [PATCH 19/31] Revert deprecated function to support PHP 7.3 --- src/Illuminate/Http/Client/RequestException.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Illuminate/Http/Client/RequestException.php b/src/Illuminate/Http/Client/RequestException.php index 5dd37e306ebc..fe2ea8be4252 100644 --- a/src/Illuminate/Http/Client/RequestException.php +++ b/src/Illuminate/Http/Client/RequestException.php @@ -2,8 +2,6 @@ namespace Illuminate\Http\Client; -use GuzzleHttp\Psr7\Message; - class RequestException extends HttpClientException { /** @@ -36,7 +34,7 @@ protected function prepareMessage(Response $response) { $message = "HTTP request returned status code {$response->status()}"; - $summary = Message::bodySummary($response->toPsrResponse()); + $summary = \GuzzleHttp\Psr7\get_message_body_summary($response->toPsrResponse()); return is_null($summary) ? $message : $message .= ":\n{$summary}\n"; } From 589fe7968349b191395547b9a0c5044a22d4456f Mon Sep 17 00:00:00 2001 From: Andrea Marco Sartori Date: Mon, 12 Apr 2021 11:00:59 +1000 Subject: [PATCH 20/31] Reset use statements order --- src/Illuminate/Http/Client/Factory.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Illuminate/Http/Client/Factory.php b/src/Illuminate/Http/Client/Factory.php index f9a507bf90a3..6fc069d390b7 100644 --- a/src/Illuminate/Http/Client/Factory.php +++ b/src/Illuminate/Http/Client/Factory.php @@ -3,13 +3,12 @@ namespace Illuminate\Http\Client; use Closure; +use function GuzzleHttp\Promise\promise_for; use GuzzleHttp\Psr7\Response as Psr7Response; use Illuminate\Support\Str; use Illuminate\Support\Traits\Macroable; use PHPUnit\Framework\Assert as PHPUnit; -use function GuzzleHttp\Promise\promise_for; - /** * @method \Illuminate\Http\Client\PendingRequest accept(string $contentType) * @method \Illuminate\Http\Client\PendingRequest acceptJson() From ce69e55d79bd9ef15eb2c0013e889a1625ece816 Mon Sep 17 00:00:00 2001 From: Andrea Marco Sartori Date: Mon, 12 Apr 2021 11:29:15 +1000 Subject: [PATCH 21/31] Fix style --- src/Illuminate/Http/Client/PendingRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Http/Client/PendingRequest.php b/src/Illuminate/Http/Client/PendingRequest.php index 79be9399c874..56f29dec2e5e 100644 --- a/src/Illuminate/Http/Client/PendingRequest.php +++ b/src/Illuminate/Http/Client/PendingRequest.php @@ -932,7 +932,7 @@ public function async(bool $async = true) } /** - * Send a pool of asynchronous requests concurrently + * Send a pool of asynchronous requests concurrently. * * @param callable $callback * @return array From d834187251af3fe31f7be92f9e5e3d196bc342d4 Mon Sep 17 00:00:00 2001 From: Bennett Treptow Date: Mon, 12 Apr 2021 07:23:06 -0500 Subject: [PATCH 22/31] [8.x] Add tinyText data type to Blueprint and to available database grammars (#36949) * Add tinyText to available grammars * Update test to also check that nullable() also works on the new type * Update style --- src/Illuminate/Database/Schema/Blueprint.php | 11 ++++ .../Database/Schema/Grammars/MySqlGrammar.php | 11 ++++ .../Schema/Grammars/PostgresGrammar.php | 11 ++++ .../Schema/Grammars/SQLiteGrammar.php | 11 ++++ .../Schema/Grammars/SqlServerGrammar.php | 11 ++++ .../Database/DatabaseSchemaBlueprintTest.php | 58 +++++++++++++++++++ 6 files changed, 113 insertions(+) diff --git a/src/Illuminate/Database/Schema/Blueprint.php b/src/Illuminate/Database/Schema/Blueprint.php index 622659995410..e2b968ab6bd4 100755 --- a/src/Illuminate/Database/Schema/Blueprint.php +++ b/src/Illuminate/Database/Schema/Blueprint.php @@ -677,6 +677,17 @@ public function string($column, $length = null) return $this->addColumn('string', $column, compact('length')); } + /** + * Create a new tiny text column on the table. + * + * @param string $column + * @return \Illuminate\Database\Schema\ColumnDefinition + */ + public function tinyText($column) + { + return $this->addColumn('tinyText', $column); + } + /** * Create a new text column on the table. * diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index dd53b9fe9bed..37df3337b040 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -490,6 +490,17 @@ protected function typeString(Fluent $column) return "varchar({$column->length})"; } + /** + * Create the column definition for a tiny text type. + * + * @param \Illuminate\Support\Fluent $column + * @return string + */ + protected function typeTinyText(Fluent $column) + { + return 'tinytext'; + } + /** * Create the column definition for a text type. * diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index adaf21f90e4c..fb7005b09df3 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -472,6 +472,17 @@ protected function typeString(Fluent $column) return "varchar({$column->length})"; } + /** + * Create the column definition for a tiny text type. + * + * @param \Illuminate\Support\Fluent $column + * @return string + */ + protected function typeTinyText(Fluent $column) + { + return 'varchar(255)'; + } + /** * Create the column definition for a text type. * diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index 556d749e23b2..b7e406f578ef 100755 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -432,6 +432,17 @@ protected function typeString(Fluent $column) return 'varchar'; } + /** + * Create the column definition for a tiny text type. + * + * @param \Illuminate\Support\Fluent $column + * @return string + */ + protected function typeTinyText(Fluent $column) + { + return 'text'; + } + /** * Create the column definition for a text type. * diff --git a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php index c3fc442e2368..b147628ec2f3 100755 --- a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php @@ -417,6 +417,17 @@ protected function typeString(Fluent $column) return "nvarchar({$column->length})"; } + /** + * Create the column definition for a tiny text type. + * + * @param \Illuminate\Support\Fluent $column + * @return string + */ + protected function typeTinyText(Fluent $column) + { + return 'nvarchar(255)'; + } + /** * Create the column definition for a text type. * diff --git a/tests/Database/DatabaseSchemaBlueprintTest.php b/tests/Database/DatabaseSchemaBlueprintTest.php index 94303415e8f4..5247df35c3ba 100755 --- a/tests/Database/DatabaseSchemaBlueprintTest.php +++ b/tests/Database/DatabaseSchemaBlueprintTest.php @@ -291,4 +291,62 @@ public function testGenerateRelationshipColumnWithUuidModel() 'alter table `posts` add `eloquent_model_uuid_stub_id` char(36) not null', ], $blueprint->toSql($connection, new MySqlGrammar)); } + + public function testTinyTextColumn() + { + $base = new Blueprint('posts', function ($table) { + $table->tinyText('note'); + }); + + $connection = m::mock(Connection::class); + + $blueprint = clone $base; + $this->assertEquals([ + 'alter table `posts` add `note` tinytext not null', + ], $blueprint->toSql($connection, new MySqlGrammar)); + + $blueprint = clone $base; + $this->assertEquals([ + 'alter table "posts" add column "note" text not null', + ], $blueprint->toSql($connection, new SQLiteGrammar)); + + $blueprint = clone $base; + $this->assertEquals([ + 'alter table "posts" add column "note" varchar(255) not null', + ], $blueprint->toSql($connection, new PostgresGrammar)); + + $blueprint = clone $base; + $this->assertEquals([ + 'alter table "posts" add "note" nvarchar(255) not null', + ], $blueprint->toSql($connection, new SqlServerGrammar)); + } + + public function testTinyTextNullableColumn() + { + $base = new Blueprint('posts', function ($table) { + $table->tinyText('note')->nullable(); + }); + + $connection = m::mock(Connection::class); + + $blueprint = clone $base; + $this->assertEquals([ + 'alter table `posts` add `note` tinytext null', + ], $blueprint->toSql($connection, new MySqlGrammar)); + + $blueprint = clone $base; + $this->assertEquals([ + 'alter table "posts" add column "note" text', + ], $blueprint->toSql($connection, new SQLiteGrammar)); + + $blueprint = clone $base; + $this->assertEquals([ + 'alter table "posts" add column "note" varchar(255) null', + ], $blueprint->toSql($connection, new PostgresGrammar)); + + $blueprint = clone $base; + $this->assertEquals([ + 'alter table "posts" add "note" nvarchar(255) null', + ], $blueprint->toSql($connection, new SqlServerGrammar)); + } } From cf439a011a2f866aa54f7b8c8258ad11d35abfa7 Mon Sep 17 00:00:00 2001 From: Mohamed Said Date: Mon, 12 Apr 2021 14:25:07 +0200 Subject: [PATCH 23/31] [8.x] Add a method to remove a resolved view engine (#36955) * add a method to remove a resolved view engine * clean --- src/Illuminate/View/Engines/EngineResolver.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Illuminate/View/Engines/EngineResolver.php b/src/Illuminate/View/Engines/EngineResolver.php index d0edb7367df6..6a5b80026342 100755 --- a/src/Illuminate/View/Engines/EngineResolver.php +++ b/src/Illuminate/View/Engines/EngineResolver.php @@ -57,4 +57,15 @@ public function resolve($engine) throw new InvalidArgumentException("Engine [{$engine}] not found."); } + + /** + * Remove a resolved engine. + * + * @param string $engine + * @return void + */ + public function forget($engine) + { + unset($this->resolved[$engine]); + } } From 245a7125076e52da7ce55b494c1c01f0f28df55d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 12 Apr 2021 10:26:57 -0500 Subject: [PATCH 24/31] formatting --- src/Illuminate/Http/Client/PendingRequest.php | 66 +++++++++---------- src/Illuminate/Http/Client/Pool.php | 3 +- tests/Http/HttpClientTest.php | 6 +- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/Illuminate/Http/Client/PendingRequest.php b/src/Illuminate/Http/Client/PendingRequest.php index 56f29dec2e5e..f0790c5a54a3 100644 --- a/src/Illuminate/Http/Client/PendingRequest.php +++ b/src/Illuminate/Http/Client/PendingRequest.php @@ -26,7 +26,7 @@ class PendingRequest protected $factory; /** - * The client instance. + * The Guzzle client instance. * * @var \GuzzleHttp\Client */ @@ -595,6 +595,27 @@ public function delete($url, $data = []) ]); } + /** + * Send a pool of asynchronous requests concurrently. + * + * @param callable $callback + * @return array + */ + public function pool(callable $callback) + { + $results = []; + + $requests = tap(new Pool($this->factory), $callback)->getRequests(); + + foreach ($requests as $key => $item) { + $results[$key] = $item instanceof static ? $item->getPromise()->wait() : $item->wait(); + } + + ksort($results); + + return $results; + } + /** * Send the request to the given URL. * @@ -742,19 +763,6 @@ protected function populateResponse(Response $response) return $response; } - /** - * Set the client instance. - * - * @param \GuzzleHttp\Client $client - * @return $this - */ - public function setClient(Client $client) - { - $this->client = $client; - - return $this; - } - /** * Build the Guzzle client. * @@ -932,33 +940,25 @@ public function async(bool $async = true) } /** - * Send a pool of asynchronous requests concurrently. + * Retrieve the pending request promise. * - * @param callable $callback - * @return array + * @return \GuzzleHttp\Promise\PromiseInterface|null */ - public function pool(callable $callback) + public function getPromise() { - $results = []; - - $requests = tap(new Pool($this->factory), $callback)->getRequests(); - - foreach ($requests as $key => $item) { - $results[$key] = $item instanceof static ? $item->getPromise()->wait() : $item->wait(); - } - - ksort($results); - - return $results; + return $this->promise; } /** - * Retrieve the pending request promise. + * Set the client instance. * - * @return \GuzzleHttp\Promise\PromiseInterface|null + * @param \GuzzleHttp\Client $client + * @return $this */ - public function getPromise() + public function setClient(Client $client) { - return $this->promise; + $this->client = $client; + + return $this; } } diff --git a/src/Illuminate/Http/Client/Pool.php b/src/Illuminate/Http/Client/Pool.php index 8b04f4300155..15002b28e92e 100644 --- a/src/Illuminate/Http/Client/Pool.php +++ b/src/Illuminate/Http/Client/Pool.php @@ -44,7 +44,7 @@ public function __construct(Factory $factory = null) * @param string $key * @return \Illuminate\Http\Client\PendingRequest */ - public function add(string $key) + public function as(string $key) { return $this->pool[$key] = $this->asyncRequest(); } @@ -56,7 +56,6 @@ public function add(string $key) */ protected function asyncRequest() { - // the same client instance needs to be shared across all async requests return $this->factory->setClient($this->client)->async(); } diff --git a/tests/Http/HttpClientTest.php b/tests/Http/HttpClientTest.php index 473a5b4e06c5..84709fa3ba66 100644 --- a/tests/Http/HttpClientTest.php +++ b/tests/Http/HttpClientTest.php @@ -892,9 +892,9 @@ public function testMultipleRequestsAreSentInThePoolWithKeys() $responses = $this->factory->pool(function (Pool $pool) { return [ - $pool->add('test200')->get('200.com'), - $pool->add('test400')->get('400.com'), - $pool->add('test500')->get('500.com'), + $pool->as('test200')->get('200.com'), + $pool->as('test400')->get('400.com'), + $pool->as('test500')->get('500.com'), ]; }); From 9a9f59fcc6e7b93465ce9848b52a473477dff64a Mon Sep 17 00:00:00 2001 From: donnysim Date: Tue, 13 Apr 2021 09:19:23 +0300 Subject: [PATCH 25/31] refactor: extract attribute getter for insert. --- src/Illuminate/Database/Eloquent/Model.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 245a103bce40..e8dea2327933 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -989,6 +989,16 @@ protected function getKeyForSaveQuery() return $this->original[$this->getKeyName()] ?? $this->getKey(); } + /** + * Get all of the current attributes on the model for insert. + * + * @return array + */ + protected function getAttributesForInsert() + { + return $this->getAttributes(); + } + /** * Perform a model insert operation. * @@ -1011,7 +1021,7 @@ protected function performInsert(Builder $query) // If the model has an incrementing key, we can use the "insertGetId" method on // the query builder, which will give us back the final inserted ID for this // table from the database. Not all tables have to be incrementing though. - $attributes = $this->getAttributes(); + $attributes = $this->getAttributesForInsert(); if ($this->getIncrementing()) { $this->insertAndSetId($query, $attributes); From 314bf875ba5d37c056ccea5148181fcb0517f596 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 13 Apr 2021 07:47:31 -0500 Subject: [PATCH 26/31] formatting' --- .../Database/Eloquent/Concerns/HasAttributes.php | 10 ++++++++++ src/Illuminate/Database/Eloquent/Model.php | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 459b14c73399..7d660905e393 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -1322,6 +1322,16 @@ public function getAttributes() return $this->attributes; } + /** + * Get all of the current attributes on the model for an insert operation. + * + * @return array + */ + protected function getAttributesForInsert() + { + return $this->getAttributes(); + } + /** * Set the array of model attributes. No checking is done. * diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index e8dea2327933..512d59eae10e 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -989,16 +989,6 @@ protected function getKeyForSaveQuery() return $this->original[$this->getKeyName()] ?? $this->getKey(); } - /** - * Get all of the current attributes on the model for insert. - * - * @return array - */ - protected function getAttributesForInsert() - { - return $this->getAttributes(); - } - /** * Perform a model insert operation. * From b72479aa710d10ad1a88ad8f550fe87f5ee7cd78 Mon Sep 17 00:00:00 2001 From: Jonathan Reinink Date: Tue, 13 Apr 2021 08:54:24 -0400 Subject: [PATCH 27/31] Make pagination linkCollection() method public (#36959) --- src/Illuminate/Pagination/LengthAwarePaginator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Pagination/LengthAwarePaginator.php b/src/Illuminate/Pagination/LengthAwarePaginator.php index 3e5adad0a316..d1c6cc711fb5 100644 --- a/src/Illuminate/Pagination/LengthAwarePaginator.php +++ b/src/Illuminate/Pagination/LengthAwarePaginator.php @@ -99,7 +99,7 @@ public function render($view = null, $data = []) * * @return \Illuminate\Support\Collection */ - protected function linkCollection() + public function linkCollection() { return collect($this->elements())->flatMap(function ($item) { if (! is_array($item)) { From e1f36fa3819c0d4b155dadcf19926d73045b9784 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 13 Apr 2021 15:06:47 +0200 Subject: [PATCH 28/31] [6.x] Fix required_if boolean validation (#36967) * Fix required_if boolean validation * Update ValidatesAttributes.php Co-authored-by: Taylor Otwell --- .../Validation/Concerns/ValidatesAttributes.php | 13 ++++++++++++- tests/Validation/ValidationValidatorTest.php | 11 +++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Validation/Concerns/ValidatesAttributes.php b/src/Illuminate/Validation/Concerns/ValidatesAttributes.php index 13fe1a648108..68b38aa8491c 100644 --- a/src/Illuminate/Validation/Concerns/ValidatesAttributes.php +++ b/src/Illuminate/Validation/Concerns/ValidatesAttributes.php @@ -1475,13 +1475,24 @@ protected function prepareValuesAndOther($parameters) $values = array_slice($parameters, 1); - if (is_bool($other)) { + if ($this->shouldConvertToBoolean($parameters[0]) || is_bool($other)) { $values = $this->convertValuesToBoolean($values); } return [$values, $other]; } + /** + * Check if parameter should be converted to boolean. + * + * @param string $parameter + * @return bool + */ + protected function shouldConvertToBoolean($parameter) + { + return in_array('boolean', Arr::get($this->rules, $parameter, [])); + } + /** * Convert the given values to boolean if they are string "true" / "false". * diff --git a/tests/Validation/ValidationValidatorTest.php b/tests/Validation/ValidationValidatorTest.php index 4ac71213c984..2938f18e92b0 100755 --- a/tests/Validation/ValidationValidatorTest.php +++ b/tests/Validation/ValidationValidatorTest.php @@ -1090,6 +1090,17 @@ public function testRequiredIf() $v = new Validator($trans, ['first' => 'dayle', 'last' => ''], ['last' => 'RequiredIf:first,taylor,dayle']); $this->assertFalse($v->passes()); $this->assertSame('The last field is required when first is dayle.', $v->messages()->first('last')); + + $trans = $this->getIlluminateArrayTranslator(); + $trans->addLines(['validation.required_if' => 'The :attribute field is required when :other is :value.'], 'en'); + $v = new Validator($trans, ['foo' => 0], [ + 'foo' => 'required|boolean', + 'bar' => 'required_if:foo,true', + 'baz' => 'required_if:foo,false', + ]); + $this->assertTrue($v->fails()); + $this->assertCount(1, $v->messages()); + $this->assertSame('The baz field is required when foo is 0.', $v->messages()->first('baz')); } public function testRequiredUnless() From b24b32e8c518b70b7f56247168a8a33ec2f2de99 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 13 Apr 2021 08:14:08 -0500 Subject: [PATCH 29/31] formatting --- src/Illuminate/Database/Migrations/Migrator.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/Migrations/Migrator.php b/src/Illuminate/Database/Migrations/Migrator.php index b51db2a142c5..35b4bb92e92a 100755 --- a/src/Illuminate/Database/Migrations/Migrator.php +++ b/src/Illuminate/Database/Migrations/Migrator.php @@ -187,6 +187,7 @@ protected function runUp($file, $batch, $pretend) // migration file name. Once we have the instances we can run the actual // command such as "up" or "down", or we can just simulate the action. $migration = $this->resolvePath($file); + $name = $this->getMigrationName($file); if ($pretend) { @@ -349,6 +350,7 @@ protected function runDown($file, $migration, $pretend) // instance of the migration. Once we get an instance we can either run a // pretend execution of the migration or we can run the real migration. $instance = $this->resolvePath($file); + $name = $this->getMigrationName($file); $this->note("Rolling back: {$name}"); @@ -413,6 +415,7 @@ protected function pretendToRun($migration, $method) $name = get_class($migration); $reflectionClass = new ReflectionClass($migration); + if ($reflectionClass->isAnonymous()) { $name = $this->getMigrationName($reflectionClass->getFileName()); } @@ -458,7 +461,7 @@ public function resolve($file) } /** - * Resolve a migration instance from migration path. + * Resolve a migration instance from a migration path. * * @param string $path * @return object @@ -466,6 +469,7 @@ public function resolve($file) protected function resolvePath(string $path) { $class = $this->getMigrationClass($this->getMigrationName($path)); + if (class_exists($class)) { return new $class; } @@ -474,7 +478,7 @@ protected function resolvePath(string $path) } /** - * Generate migration class name based on migration name. + * Generate a migration class name based on the migration file name. * * @param string $migrationName * @return string From 63655bf44ad7c3b2483c4a3c112831326f92e68c Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Tue, 13 Apr 2021 15:39:47 +0200 Subject: [PATCH 30/31] Revert "[6.x] Fix required_if boolean validation (#36967)" This reverts commit e1f36fa3819c0d4b155dadcf19926d73045b9784. --- .../Validation/Concerns/ValidatesAttributes.php | 13 +------------ tests/Validation/ValidationValidatorTest.php | 11 ----------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/Illuminate/Validation/Concerns/ValidatesAttributes.php b/src/Illuminate/Validation/Concerns/ValidatesAttributes.php index 68b38aa8491c..13fe1a648108 100644 --- a/src/Illuminate/Validation/Concerns/ValidatesAttributes.php +++ b/src/Illuminate/Validation/Concerns/ValidatesAttributes.php @@ -1475,24 +1475,13 @@ protected function prepareValuesAndOther($parameters) $values = array_slice($parameters, 1); - if ($this->shouldConvertToBoolean($parameters[0]) || is_bool($other)) { + if (is_bool($other)) { $values = $this->convertValuesToBoolean($values); } return [$values, $other]; } - /** - * Check if parameter should be converted to boolean. - * - * @param string $parameter - * @return bool - */ - protected function shouldConvertToBoolean($parameter) - { - return in_array('boolean', Arr::get($this->rules, $parameter, [])); - } - /** * Convert the given values to boolean if they are string "true" / "false". * diff --git a/tests/Validation/ValidationValidatorTest.php b/tests/Validation/ValidationValidatorTest.php index 2938f18e92b0..4ac71213c984 100755 --- a/tests/Validation/ValidationValidatorTest.php +++ b/tests/Validation/ValidationValidatorTest.php @@ -1090,17 +1090,6 @@ public function testRequiredIf() $v = new Validator($trans, ['first' => 'dayle', 'last' => ''], ['last' => 'RequiredIf:first,taylor,dayle']); $this->assertFalse($v->passes()); $this->assertSame('The last field is required when first is dayle.', $v->messages()->first('last')); - - $trans = $this->getIlluminateArrayTranslator(); - $trans->addLines(['validation.required_if' => 'The :attribute field is required when :other is :value.'], 'en'); - $v = new Validator($trans, ['foo' => 0], [ - 'foo' => 'required|boolean', - 'bar' => 'required_if:foo,true', - 'baz' => 'required_if:foo,false', - ]); - $this->assertTrue($v->fails()); - $this->assertCount(1, $v->messages()); - $this->assertSame('The baz field is required when foo is 0.', $v->messages()->first('baz')); } public function testRequiredUnless() From cf4082973abc796ec285190f0603380021f6d26f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 13 Apr 2021 08:49:49 -0500 Subject: [PATCH 31/31] wip --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index f7d1716456d0..b9a9eecbbca9 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -33,7 +33,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '8.36.2'; + const VERSION = '8.37.0'; /** * The base path for the Laravel installation. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy