├── .github └── workflows │ └── php.yml ├── .gitignore ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Facade.php └── JobChainer.php └── tests ├── JobChainerTest.php ├── Jobs ├── A.php ├── B.php └── C.php └── TestCase.php /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | fail-fast: true 15 | matrix: 16 | os: [ubuntu-latest] 17 | php: [7.4, 8.0] 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - name: Validate composer.json and composer.lock 23 | run: composer validate 24 | 25 | - name: Cache Composer packages 26 | id: composer-cache 27 | uses: actions/cache@v2 28 | with: 29 | path: vendor 30 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 31 | restore-keys: | 32 | ${{ runner.os }}-php- 33 | - name: Install dependencies 34 | if: steps.composer-cache.outputs.cache-hit != 'true' 35 | run: composer install --prefer-dist --no-progress --no-suggest 36 | 37 | - name: Run test suite 38 | run: composer run-script test 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .phpunit.result.cache 3 | composer.lock 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Packagist Downloads](https://img.shields.io/packagist/dt/JustIversen/laravel-job-chainer) 2 | ![Code size](https://img.shields.io/github/languages/code-size/JustIversen/laravel-job-chainer) 3 | ![Build Status](https://img.shields.io/github/workflow/status/JustIversen/laravel-job-chainer/PHP%20Composer) 4 | 5 | # Laravel Job Chainer 6 | 7 | JobChainer does chain a variable amount of jobs by adding them 8 | with the add() method. 9 | 10 | This makes it possible to chain jobs without having to know 11 | which job will be the first to be fired. 12 | 13 | *Normal job chaining* 14 | 15 | ```php 16 | ProcessPodcast::withChain([ 17 | new OptimizePodcast, 18 | new ReleasePodcast($argA, $argB) 19 | ])->dispatch($arg1); 20 | ``` 21 | 22 | *With Job Chainer* 23 | 24 | ```php 25 | $chain = new JobChainer; 26 | 27 | $chain->add(ProcessPodcast::class, $arg1); 28 | $chain->add(OptimizePodcast::class); 29 | $chain->add(ReleasePodcast::class, $argA, $argB); 30 | 31 | $chain->dispatch(); 32 | ``` 33 | 34 | # Why? 35 | 36 | This allows us to add jobs to the chain without prior knowledge about which job would be the first. 37 | This may come in handy when jobs must be chained, but they are added dynamically to the chain. 38 | 39 | # Issue 40 | 41 | Please open a new issue, if you are experiencing any troubles. 42 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "justiversen/laravel-job-chainer", 3 | "description": "Chain Laravel jobs without having to glue it to a starting job", 4 | "keywords": [ 5 | "Laravel job chainer", 6 | "justiversen" 7 | ], 8 | "homepage": "https://github.com/justiversen/laravel-job-chainer", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Jens Just Iversen", 13 | "email": "jens@justiversen.dk", 14 | "homepage": "https://justiversen.dk", 15 | "role": "Developer" 16 | }, 17 | { 18 | "name": "Kristian Just", 19 | "email": "kristian@justiversen.dk", 20 | "homepage": "https://justiversen.dk", 21 | "role": "Developer" 22 | } 23 | ], 24 | "require": { 25 | "php": "^7.4|^8.0" 26 | }, 27 | "require-dev": { 28 | "orchestra/testbench": "^4.0|^6.6", 29 | "phpunit/phpunit": "^8.0|^9.3" 30 | }, 31 | "autoload": { 32 | "psr-4": { 33 | "JustIversen\\JobChainer\\": "src" 34 | } 35 | }, 36 | "autoload-dev": { 37 | "psr-4": { 38 | "JustIversen\\JobChainer\\Tests\\": "tests" 39 | } 40 | }, 41 | "scripts": { 42 | "test": "vendor/bin/phpunit" 43 | }, 44 | "extra": { 45 | "laravel": { 46 | "aliases": { 47 | "JobChainer": "JustIversen\\JobChainer\\Facade" 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./app 6 | 7 | 8 | 9 | 10 | ./tests 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Facade.php: -------------------------------------------------------------------------------- 1 | jobs[] = [$job, $args]; 19 | 20 | return $this; 21 | } 22 | 23 | /** 24 | * Remove job as [MyJob::class, [$arg1, $arg2]] 25 | * 26 | * @param string $job 27 | * @param mixed $args,... 28 | * @return object 29 | */ 30 | public function remove($job, ...$args) 31 | { 32 | if (($key = array_search([$job, $args], $this->jobs)) !== false) { 33 | unset($this->jobs[$key]); 34 | } 35 | 36 | return $this; 37 | } 38 | 39 | /** 40 | * Dispatch job chain 41 | * 42 | * @return \Illuminate\Foundation\Bus\PendingDispatch 43 | */ 44 | public function dispatch() 45 | { 46 | if (count($this->jobs) === 0) { 47 | return; 48 | } 49 | 50 | $inside = array_map(function ($item) { 51 | return new $item[0](...$item[1]); 52 | }, array_slice($this->jobs, 1)); 53 | 54 | $first = $this->jobs[0]; 55 | 56 | return $first[0]::withChain($inside)->dispatch(...$first[1]); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/JobChainerTest.php: -------------------------------------------------------------------------------- 1 | add(A::class); 20 | $chain->add(B::class); 21 | $chain->add(C::class); 22 | $chain->dispatch(); 23 | 24 | Queue::assertPushedWithChain(A::class, [ 25 | B::class, 26 | C::class 27 | ]); 28 | } 29 | } -------------------------------------------------------------------------------- /tests/Jobs/A.php: -------------------------------------------------------------------------------- 1 |