Skip to content

Commit 892163c

Browse files
Merge branch '8.x'
2 parents 08728a3 + 361813c commit 892163c

File tree

14 files changed

+813
-4
lines changed

14 files changed

+813
-4
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Console;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Contracts\Events\Dispatcher;
7+
use Illuminate\Database\Eloquent\MassPrunable;
8+
use Illuminate\Database\Eloquent\Prunable;
9+
use Illuminate\Database\Events\ModelsPruned;
10+
use Illuminate\Support\Str;
11+
use Symfony\Component\Finder\Finder;
12+
13+
class PruneCommand extends Command
14+
{
15+
/**
16+
* The console command name.
17+
*
18+
* @var string
19+
*/
20+
protected $signature = 'model:prune
21+
{--model=* : Class names of the models to be pruned}
22+
{--chunk=1000 : The number of models to retrieve per chunk of models to be deleted}';
23+
24+
/**
25+
* The console command description.
26+
*
27+
* @var string
28+
*/
29+
protected $description = 'Prune models that are no longer needed';
30+
31+
/**
32+
* Execute the console command.
33+
*
34+
* @param \Illuminate\Contracts\Events\Dispatcher $events
35+
* @return void
36+
*/
37+
public function handle(Dispatcher $events)
38+
{
39+
$events->listen(ModelsPruned::class, function ($event) {
40+
$this->info("{$event->count} [{$event->model}] records have been pruned.");
41+
});
42+
43+
$this->models()->each(function ($model) {
44+
$instance = new $model;
45+
46+
$chunkSize = property_exists($instance, 'prunableChunkSize')
47+
? $instance->prunableChunkSize
48+
: $this->option('chunk');
49+
50+
$total = $this->isPrunable($model)
51+
? $instance->pruneAll($chunkSize)
52+
: 0;
53+
54+
if ($total == 0) {
55+
$this->info("No prunable [$model] records found.");
56+
}
57+
});
58+
59+
$events->forget(ModelsPruned::class);
60+
}
61+
62+
/**
63+
* Determine the models that should be pruned.
64+
*
65+
* @return array
66+
*/
67+
protected function models()
68+
{
69+
if (! empty($models = $this->option('model'))) {
70+
return collect($models);
71+
}
72+
73+
return collect((new Finder)->in(app_path('Models'))->files())
74+
->map(function ($model) {
75+
$namespace = $this->laravel->getNamespace();
76+
77+
return $namespace.str_replace(
78+
['/', '.php'],
79+
['\\', ''],
80+
Str::after($model->getRealPath(), realpath(app_path()).DIRECTORY_SEPARATOR)
81+
);
82+
})->filter(function ($model) {
83+
return $this->isPrunable($model);
84+
})->values();
85+
}
86+
87+
/**
88+
* Determine if the given model class is prunable.
89+
*
90+
* @param string $model
91+
* @return bool
92+
*/
93+
protected function isPrunable($model)
94+
{
95+
$uses = class_uses_recursive($model);
96+
97+
return in_array(Prunable::class, $uses) || in_array(MassPrunable::class, $uses);
98+
}
99+
}

src/Illuminate/Database/Eloquent/Collection.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ protected function loadMissingRelation(self $models, array $path)
232232
return;
233233
}
234234

235-
$models = $models->pluck($name);
235+
$models = $models->pluck($name)->whereNotNull();
236236

237237
if ($models->first() instanceof BaseCollection) {
238238
$models = $models->collapse();
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Eloquent;
4+
5+
use Illuminate\Database\Events\ModelsPruned;
6+
use LogicException;
7+
8+
trait MassPrunable
9+
{
10+
/**
11+
* Prune all prunable models in the database.
12+
*
13+
* @param int $chunkSize
14+
* @return int
15+
*/
16+
public function pruneAll(int $chunkSize = 1000)
17+
{
18+
$query = tap($this->prunable(), function ($query) use ($chunkSize) {
19+
$query->when(! $query->getQuery()->limit, function ($query) use ($chunkSize) {
20+
$query->limit($chunkSize);
21+
});
22+
});
23+
24+
$total = 0;
25+
26+
do {
27+
$total += $count = in_array(SoftDeletes::class, class_uses_recursive(get_class($this)))
28+
? $query->forceDelete()
29+
: $query->delete();
30+
31+
if ($count > 0) {
32+
event(new ModelsPruned(static::class, $total));
33+
}
34+
} while ($count > 0);
35+
36+
return $total;
37+
}
38+
39+
/**
40+
* Get the prunable model query.
41+
*
42+
* @return \Illuminate\Database\Eloquent\Builder
43+
*/
44+
public function prunable()
45+
{
46+
throw new LogicException('Please implement the prunable method on your model.');
47+
}
48+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Eloquent;
4+
5+
use Illuminate\Database\Events\ModelsPruned;
6+
use LogicException;
7+
8+
trait Prunable
9+
{
10+
/**
11+
* Prune all prunable models in the database.
12+
*
13+
* @param int $chunkSize
14+
* @return int
15+
*/
16+
public function pruneAll(int $chunkSize = 1000)
17+
{
18+
$total = 0;
19+
20+
$this->prunable()
21+
->when(in_array(SoftDeletes::class, class_uses_recursive(get_class($this))), function ($query) {
22+
$query->withTrashed();
23+
})->chunkById($chunkSize, function ($models) use (&$total) {
24+
$models->each->prune();
25+
26+
$total += $models->count();
27+
28+
event(new ModelsPruned(static::class, $total));
29+
});
30+
31+
return $total;
32+
}
33+
34+
/**
35+
* Get the prunable model query.
36+
*
37+
* @return \Illuminate\Database\Eloquent\Builder
38+
*/
39+
public function prunable()
40+
{
41+
throw new LogicException('Please implement the prunable method on your model.');
42+
}
43+
44+
/**
45+
* Prune the model in the database.
46+
*
47+
* @return bool|null
48+
*/
49+
public function prune()
50+
{
51+
$this->pruning();
52+
53+
return in_array(SoftDeletes::class, class_uses_recursive(get_class($this)))
54+
? $this->forceDelete()
55+
: $this->delete();
56+
}
57+
58+
/**
59+
* Prepare the model for pruning.
60+
*
61+
* @return void
62+
*/
63+
protected function pruning()
64+
{
65+
//
66+
}
67+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Events;
4+
5+
class ModelsPruned
6+
{
7+
/**
8+
* The class name of the model that was pruned.
9+
*
10+
* @var string
11+
*/
12+
public $model;
13+
14+
/**
15+
* The number of pruned records.
16+
*
17+
* @var int
18+
*/
19+
public $count;
20+
21+
/**
22+
* Create a new event instance.
23+
*
24+
* @param string $model
25+
* @param int $count
26+
* @return void
27+
*/
28+
public function __construct($model, $count)
29+
{
30+
$this->model = $model;
31+
$this->count = $count;
32+
}
33+
}

src/Illuminate/Database/Schema/ForeignIdColumnDefinition.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public function __construct(Blueprint $blueprint, $attributes = [])
3232
*
3333
* @param string|null $table
3434
* @param string $column
35-
* @return \Illuminate\Support\Fluent|\Illuminate\Database\Schema\ForeignKeyDefinition
35+
* @return \Illuminate\Database\Schema\ForeignKeyDefinition
3636
*/
3737
public function constrained($table = null, $column = 'id')
3838
{
@@ -43,7 +43,7 @@ public function constrained($table = null, $column = 'id')
4343
* Specify which column this foreign ID references on another table.
4444
*
4545
* @param string $column
46-
* @return \Illuminate\Support\Fluent|\Illuminate\Database\Schema\ForeignKeyDefinition
46+
* @return \Illuminate\Database\Schema\ForeignKeyDefinition
4747
*/
4848
public function references($column)
4949
{

src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Illuminate\Database\Console\DbCommand;
1616
use Illuminate\Database\Console\DumpCommand;
1717
use Illuminate\Database\Console\Factories\FactoryMakeCommand;
18+
use Illuminate\Database\Console\PruneCommand;
1819
use Illuminate\Database\Console\Seeds\SeedCommand;
1920
use Illuminate\Database\Console\Seeds\SeederMakeCommand;
2021
use Illuminate\Database\Console\WipeCommand;
@@ -95,6 +96,7 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid
9596
'ConfigCache' => ConfigCacheCommand::class,
9697
'ConfigClear' => ConfigClearCommand::class,
9798
'Db' => DbCommand::class,
99+
'DbPrune' => Prune::class,
98100
'DbWipe' => WipeCommand::class,
99101
'Down' => DownCommand::class,
100102
'Environment' => EnvironmentCommand::class,
@@ -350,6 +352,16 @@ protected function registerDbCommand()
350352
$this->app->singleton(DbCommand::class);
351353
}
352354

355+
/**
356+
* Register the command.
357+
*
358+
* @return void
359+
*/
360+
protected function registerDbPruneCommand()
361+
{
362+
$this->app->singleton(PruneCommand::class);
363+
}
364+
353365
/**
354366
* Register the command.
355367
*

src/Illuminate/Http/Client/PendingRequest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,10 @@ protected function makePromise(string $method, string $url, array $options = [])
707707
{
708708
return $this->promise = $this->sendRequest($method, $url, $options)
709709
->then(function (MessageInterface $message) {
710-
return $this->populateResponse(new Response($message));
710+
return tap(new Response($message), function ($response) {
711+
$this->populateResponse($response);
712+
$this->dispatchResponseReceivedEvent($response);
713+
});
711714
})
712715
->otherwise(function (TransferException $e) {
713716
return $e instanceof RequestException ? $this->populateResponse(new Response($e->getResponse())) : $e;

src/Illuminate/Notifications/NotificationSender.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ protected function sendToNotifiable($notifiable, $id, $notification, $channel)
162162
*/
163163
protected function shouldSendNotification($notifiable, $notification, $channel)
164164
{
165+
if (method_exists($notification, 'shouldSend') &&
166+
$notification->shouldSend($notifiable, $channel) === false) {
167+
return false;
168+
}
169+
165170
return $this->events->until(
166171
new NotificationSending($notifiable, $notification, $channel)
167172
) !== false;

0 commit comments

Comments
 (0)