PendingBatch.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. <?php
  2. namespace Illuminate\Bus;
  3. use Closure;
  4. use Illuminate\Bus\Events\BatchDispatched;
  5. use Illuminate\Contracts\Container\Container;
  6. use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
  7. use Illuminate\Support\Arr;
  8. use Illuminate\Support\Collection;
  9. use Illuminate\Support\Traits\Conditionable;
  10. use Laravel\SerializableClosure\SerializableClosure;
  11. use Throwable;
  12. class PendingBatch
  13. {
  14. use Conditionable;
  15. /**
  16. * The IoC container instance.
  17. *
  18. * @var \Illuminate\Contracts\Container\Container
  19. */
  20. protected $container;
  21. /**
  22. * The batch name.
  23. *
  24. * @var string
  25. */
  26. public $name = '';
  27. /**
  28. * The jobs that belong to the batch.
  29. *
  30. * @var \Illuminate\Support\Collection
  31. */
  32. public $jobs;
  33. /**
  34. * The batch options.
  35. *
  36. * @var array
  37. */
  38. public $options = [];
  39. /**
  40. * Create a new pending batch instance.
  41. *
  42. * @param \Illuminate\Contracts\Container\Container $container
  43. * @param \Illuminate\Support\Collection $jobs
  44. * @return void
  45. */
  46. public function __construct(Container $container, Collection $jobs)
  47. {
  48. $this->container = $container;
  49. $this->jobs = $jobs;
  50. }
  51. /**
  52. * Add jobs to the batch.
  53. *
  54. * @param iterable|object|array $jobs
  55. * @return $this
  56. */
  57. public function add($jobs)
  58. {
  59. $jobs = is_iterable($jobs) ? $jobs : Arr::wrap($jobs);
  60. foreach ($jobs as $job) {
  61. $this->jobs->push($job);
  62. }
  63. return $this;
  64. }
  65. /**
  66. * Add a callback to be executed when the batch is stored.
  67. *
  68. * @param callable $callback
  69. * @return $this
  70. */
  71. public function before($callback)
  72. {
  73. $this->options['before'][] = $callback instanceof Closure
  74. ? new SerializableClosure($callback)
  75. : $callback;
  76. return $this;
  77. }
  78. /**
  79. * Get the "before" callbacks that have been registered with the pending batch.
  80. *
  81. * @return array
  82. */
  83. public function beforeCallbacks()
  84. {
  85. return $this->options['before'] ?? [];
  86. }
  87. /**
  88. * Add a callback to be executed after a job in the batch have executed successfully.
  89. *
  90. * @param callable $callback
  91. * @return $this
  92. */
  93. public function progress($callback)
  94. {
  95. $this->options['progress'][] = $callback instanceof Closure
  96. ? new SerializableClosure($callback)
  97. : $callback;
  98. return $this;
  99. }
  100. /**
  101. * Get the "progress" callbacks that have been registered with the pending batch.
  102. *
  103. * @return array
  104. */
  105. public function progressCallbacks()
  106. {
  107. return $this->options['progress'] ?? [];
  108. }
  109. /**
  110. * Add a callback to be executed after all jobs in the batch have executed successfully.
  111. *
  112. * @param callable $callback
  113. * @return $this
  114. */
  115. public function then($callback)
  116. {
  117. $this->options['then'][] = $callback instanceof Closure
  118. ? new SerializableClosure($callback)
  119. : $callback;
  120. return $this;
  121. }
  122. /**
  123. * Get the "then" callbacks that have been registered with the pending batch.
  124. *
  125. * @return array
  126. */
  127. public function thenCallbacks()
  128. {
  129. return $this->options['then'] ?? [];
  130. }
  131. /**
  132. * Add a callback to be executed after the first failing job in the batch.
  133. *
  134. * @param callable $callback
  135. * @return $this
  136. */
  137. public function catch($callback)
  138. {
  139. $this->options['catch'][] = $callback instanceof Closure
  140. ? new SerializableClosure($callback)
  141. : $callback;
  142. return $this;
  143. }
  144. /**
  145. * Get the "catch" callbacks that have been registered with the pending batch.
  146. *
  147. * @return array
  148. */
  149. public function catchCallbacks()
  150. {
  151. return $this->options['catch'] ?? [];
  152. }
  153. /**
  154. * Add a callback to be executed after the batch has finished executing.
  155. *
  156. * @param callable $callback
  157. * @return $this
  158. */
  159. public function finally($callback)
  160. {
  161. $this->options['finally'][] = $callback instanceof Closure
  162. ? new SerializableClosure($callback)
  163. : $callback;
  164. return $this;
  165. }
  166. /**
  167. * Get the "finally" callbacks that have been registered with the pending batch.
  168. *
  169. * @return array
  170. */
  171. public function finallyCallbacks()
  172. {
  173. return $this->options['finally'] ?? [];
  174. }
  175. /**
  176. * Indicate that the batch should not be cancelled when a job within the batch fails.
  177. *
  178. * @param bool $allowFailures
  179. * @return $this
  180. */
  181. public function allowFailures($allowFailures = true)
  182. {
  183. $this->options['allowFailures'] = $allowFailures;
  184. return $this;
  185. }
  186. /**
  187. * Determine if the pending batch allows jobs to fail without cancelling the batch.
  188. *
  189. * @return bool
  190. */
  191. public function allowsFailures()
  192. {
  193. return Arr::get($this->options, 'allowFailures', false) === true;
  194. }
  195. /**
  196. * Set the name for the batch.
  197. *
  198. * @param string $name
  199. * @return $this
  200. */
  201. public function name(string $name)
  202. {
  203. $this->name = $name;
  204. return $this;
  205. }
  206. /**
  207. * Specify the queue connection that the batched jobs should run on.
  208. *
  209. * @param string $connection
  210. * @return $this
  211. */
  212. public function onConnection(string $connection)
  213. {
  214. $this->options['connection'] = $connection;
  215. return $this;
  216. }
  217. /**
  218. * Get the connection used by the pending batch.
  219. *
  220. * @return string|null
  221. */
  222. public function connection()
  223. {
  224. return $this->options['connection'] ?? null;
  225. }
  226. /**
  227. * Specify the queue that the batched jobs should run on.
  228. *
  229. * @param string $queue
  230. * @return $this
  231. */
  232. public function onQueue(string $queue)
  233. {
  234. $this->options['queue'] = $queue;
  235. return $this;
  236. }
  237. /**
  238. * Get the queue used by the pending batch.
  239. *
  240. * @return string|null
  241. */
  242. public function queue()
  243. {
  244. return $this->options['queue'] ?? null;
  245. }
  246. /**
  247. * Add additional data into the batch's options array.
  248. *
  249. * @param string $key
  250. * @param mixed $value
  251. * @return $this
  252. */
  253. public function withOption(string $key, $value)
  254. {
  255. $this->options[$key] = $value;
  256. return $this;
  257. }
  258. /**
  259. * Dispatch the batch.
  260. *
  261. * @return \Illuminate\Bus\Batch
  262. *
  263. * @throws \Throwable
  264. */
  265. public function dispatch()
  266. {
  267. $repository = $this->container->make(BatchRepository::class);
  268. try {
  269. $batch = $this->store($repository);
  270. $batch = $batch->add($this->jobs);
  271. } catch (Throwable $e) {
  272. if (isset($batch)) {
  273. $repository->delete($batch->id);
  274. }
  275. throw $e;
  276. }
  277. $this->container->make(EventDispatcher::class)->dispatch(
  278. new BatchDispatched($batch)
  279. );
  280. return $batch;
  281. }
  282. /**
  283. * Dispatch the batch after the response is sent to the browser.
  284. *
  285. * @return \Illuminate\Bus\Batch
  286. */
  287. public function dispatchAfterResponse()
  288. {
  289. $repository = $this->container->make(BatchRepository::class);
  290. $batch = $this->store($repository);
  291. if ($batch) {
  292. $this->container->terminating(function () use ($batch) {
  293. $this->dispatchExistingBatch($batch);
  294. });
  295. }
  296. return $batch;
  297. }
  298. /**
  299. * Dispatch an existing batch.
  300. *
  301. * @param \Illuminate\Bus\Batch $batch
  302. * @return void
  303. *
  304. * @throws \Throwable
  305. */
  306. protected function dispatchExistingBatch($batch)
  307. {
  308. try {
  309. $batch = $batch->add($this->jobs);
  310. } catch (Throwable $e) {
  311. if (isset($batch)) {
  312. $batch->delete();
  313. }
  314. throw $e;
  315. }
  316. $this->container->make(EventDispatcher::class)->dispatch(
  317. new BatchDispatched($batch)
  318. );
  319. }
  320. /**
  321. * Dispatch the batch if the given truth test passes.
  322. *
  323. * @param bool|\Closure $boolean
  324. * @return \Illuminate\Bus\Batch|null
  325. */
  326. public function dispatchIf($boolean)
  327. {
  328. return value($boolean) ? $this->dispatch() : null;
  329. }
  330. /**
  331. * Dispatch the batch unless the given truth test passes.
  332. *
  333. * @param bool|\Closure $boolean
  334. * @return \Illuminate\Bus\Batch|null
  335. */
  336. public function dispatchUnless($boolean)
  337. {
  338. return ! value($boolean) ? $this->dispatch() : null;
  339. }
  340. /**
  341. * Store the batch using the given repository.
  342. *
  343. * @param \Illuminate\Bus\BatchRepository $repository
  344. * @return \Illuminate\Bus\Batch
  345. */
  346. protected function store($repository)
  347. {
  348. $batch = $repository->store($this);
  349. collect($this->beforeCallbacks())->each(function ($handler) use ($batch) {
  350. try {
  351. return $handler($batch);
  352. } catch (Throwable $e) {
  353. if (function_exists('report')) {
  354. report($e);
  355. }
  356. }
  357. });
  358. return $batch;
  359. }
  360. }