├── .codecov.yml
├── .github
└── workflows
│ └── tests.yml
├── .gitignore
├── .phpunit.result.cache
├── .styleci.yml
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── clover.xml
├── composer.json
├── composer.lock
├── config
└── pipes.php
├── phpunit.xml
├── readme.md
├── routes
└── api.php
├── src
├── Contracts
│ └── Registrar.php
├── Events
│ ├── IncomingPipeRequest.php
│ └── IncomingPipeResponse.php
├── Exceptions
│ ├── NoKeysSpecifiedException.php
│ └── NotFoundPipeException.php
├── Facades
│ └── Pipe.php
├── Http
│ └── Middleware
│ │ └── SubstituteBindings.php
├── Jobs
│ └── ExecutePipeRequest.php
├── Kernel.php
├── LaravelPipesServiceProvider.php
├── Matching
│ ├── CueValidator.php
│ ├── KeyValidator.php
│ ├── PatternValidator.php
│ └── ValidatorInterface.php
├── Pipe.php
├── PipeAction.php
├── PipeCollection.php
├── PipeGroup.php
├── PipeParameterBinder.php
├── PipeRegistrar.php
├── Piper.php
├── Request.php
├── Response.php
└── Testing
│ ├── Fakes
│ └── PipeFake.php
│ └── MakesPipeRequests.php
└── tests
├── Fixtures
├── Controllers
│ └── TestController.php
├── Migrations
│ └── 2014_10_12_000000_create_todos_table.php
├── Models
│ └── Todo.php
└── pipes.php
├── PipeRequestTest.php
├── SubstituteBindingsTest.php
└── TestCase.php
/.codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | notify:
3 | require_ci_to_pass: yes
4 |
5 | coverage:
6 | precision: 2
7 | round: down
8 | range: “70…100”
9 |
10 | status:
11 | project: yes
12 | patch: yes
13 | changes: no
14 |
15 | comment:
16 | layout: "reach, diff, flags, files, footer"
17 | behavior: default
18 | require_changes: no
19 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: 'Laravel Tests'
2 |
3 | on:
4 | push:
5 | pull_request:
6 | schedule:
7 | - cron: '0 0 * * *'
8 |
9 | jobs:
10 | tests-on-php71:
11 | runs-on: ubuntu-latest
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | php: [7.1]
16 | laravel: [5.8.x]
17 |
18 | name: PHP${{ matrix.php }} with Laravel ${{ matrix.laravel }}
19 |
20 | steps:
21 | - name: Checkout code
22 | uses: actions/checkout@v1
23 |
24 | - name: Cache dependencies
25 | uses: actions/cache@v2
26 | with:
27 | path: ~/.composer/cache/files
28 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
29 |
30 | - name: Setup PHP
31 | uses: shivammathur/setup-php@v2
32 | with:
33 | php-version: ${{ matrix.php }}
34 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath
35 | coverage: none
36 |
37 | - name: Install dependencies
38 | run: |
39 | composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
40 | composer update --prefer-dist --no-interaction --no-suggest
41 | - name: Execute tests
42 | run: vendor/bin/phpunit --exclude-group=redis-auth
43 | env:
44 | REDIS_HOST: redis
45 |
46 | tests-on-php72:
47 | runs-on: ubuntu-latest
48 | strategy:
49 | fail-fast: false
50 | matrix:
51 | php: [7.2]
52 | laravel: [7.x, 6.x, 5.8.x]
53 |
54 | name: PHP${{ matrix.php }} with Laravel ${{ matrix.laravel }}
55 |
56 | steps:
57 | - name: Checkout code
58 | uses: actions/checkout@v1
59 |
60 | - name: Cache dependencies
61 | uses: actions/cache@v2
62 | with:
63 | path: ~/.composer/cache/files
64 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
65 |
66 | - name: Setup PHP
67 | uses: shivammathur/setup-php@v2
68 | with:
69 | php-version: ${{ matrix.php }}
70 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath
71 | coverage: none
72 |
73 | - name: Install dependencies
74 | run: |
75 | composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
76 | composer update --prefer-dist --no-interaction --no-suggest
77 | - name: Execute tests
78 | run: vendor/bin/phpunit --exclude-group=redis-auth
79 | env:
80 | REDIS_HOST: redis
81 |
82 |
83 | tests-on-php73:
84 | runs-on: ubuntu-latest
85 | strategy:
86 | fail-fast: false
87 | matrix:
88 | php: [7.3]
89 | laravel: [8.x, 7.x, 6.x, 5.8.x]
90 |
91 | name: PHP${{ matrix.php }} with Laravel ${{ matrix.laravel }}
92 |
93 | steps:
94 | - name: Checkout code
95 | uses: actions/checkout@v1
96 |
97 | - name: Cache dependencies
98 | uses: actions/cache@v2
99 | with:
100 | path: ~/.composer/cache/files
101 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
102 |
103 | - name: Setup PHP
104 | uses: shivammathur/setup-php@v2
105 | with:
106 | php-version: ${{ matrix.php }}
107 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath
108 | coverage: none
109 |
110 | - name: Install dependencies
111 | run: |
112 | composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
113 | composer update --prefer-dist --no-interaction --no-suggest
114 | - name: Execute tests
115 | run: vendor/bin/phpunit --exclude-group=redis-auth
116 | env:
117 | REDIS_HOST: redis
118 |
119 | tests-on-php74:
120 | runs-on: ubuntu-latest
121 | strategy:
122 | fail-fast: false
123 | matrix:
124 | php: [7.4]
125 | laravel: [8.x, 7.x, 6.x, 5.8.x]
126 |
127 | name: PHP${{ matrix.php }} with Laravel ${{ matrix.laravel }}
128 |
129 | steps:
130 | - name: Checkout code
131 | uses: actions/checkout@v1
132 |
133 | - name: Cache dependencies
134 | uses: actions/cache@v2
135 | with:
136 | path: ~/.composer/cache/files
137 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
138 |
139 | - name: Setup PHP
140 | uses: shivammathur/setup-php@v2
141 | with:
142 | php-version: ${{ matrix.php }}
143 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath
144 | coverage: none
145 |
146 | - name: Install dependencies
147 | run: |
148 | composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
149 | composer update --prefer-dist --no-interaction --no-suggest
150 | - name: Execute tests
151 | run: vendor/bin/phpunit --exclude-group=redis-auth
152 | env:
153 | REDIS_HOST: redis
154 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /vendor
--------------------------------------------------------------------------------
/.phpunit.result.cache:
--------------------------------------------------------------------------------
1 | C:37:"PHPUnit\Runner\DefaultTestResultCache":5534:{a:2:{s:7:"defects";a:24:{s:128:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_identify_aliases_of_pipes_even_when_the_request_does_only_start_with_the_alias";i:3;s:78:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_bind_models_to_pipe_requests";i:4;s:73:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_returns_a_ok_200er_response";i:3;s:100:"Mshule\LaravelPipes\Tests\PipeRequestTest::the_response_can_be_changed_through_the_response_resolver";i:3;s:125:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_fires_an_incoming_pipe_response_event_when_a_response_is_returned_by_the_kernel";i:3;s:76:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_resolve_pipes_to_callbacks";i:3;s:77:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_matches_everything_to_lowercase";i:3;s:85:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_resolve_pipes_to_controller_actions";i:3;s:114:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_resolve_pipes_to_controller_actions_through_using_the_fluent_api";i:3;s:80:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_resolve_pipes_with_middlewares";i:3;s:111:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_resolve_grouped_pipes_and_pass_all_key_to_the_contained_pipes";i:3;s:127:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_add_fallback_pipes_to_handle_any_request_which_could_not_be_matched_otherwise";i:3;s:74:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_match_dynamic_parameters";i:4;s:140:"Mshule\LaravelPipes\Tests\PipeRequestTest::its_dynamic_pattern_match_checks_first_for_placeholder_name_in_request_then_for_default_cue_value";i:3;s:83:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_match_multiple_dynamic_parameters";i:4;s:89:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_match_dynamic_parameters_to_any_request";i:3;s:123:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_doesnt_match_dynamic_parameters_if_the_request_contains_only_parts_of_the_cue";i:3;s:84:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_add_conditions_to_pipe_definitions";i:3;s:70:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_add_aliases_to_pipes";i:3;s:81:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_add_aliases_predefined_to_pipes";i:3;s:71:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_load_pipes_from_files";i:3;s:84:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_ignores_global_helper_functions_as_cue";i:3;s:110:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_does_not_match_one_character_to_a_pipe_if_it_is_no_dynamic_param";i:3;s:85:"Mshule\LaravelPipes\Tests\SubstituteBindingsTest::it_can_bind_models_to_pipe_requests";i:3;}s:5:"times";a:26:{s:106:"Mshule\LaravelPipes\Tests\PipeRequestTest::a_not_found_pipe_exception_is_thrown_if_no_controller_was_found";d:0.068;s:73:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_returns_a_ok_200er_response";d:0.015;s:100:"Mshule\LaravelPipes\Tests\PipeRequestTest::the_response_can_be_changed_through_the_response_resolver";d:0.005;s:122:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_fires_an_incoming_pipe_request_event_when_a_request_is_handled_by_the_kernel";d:0.006;s:125:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_fires_an_incoming_pipe_response_event_when_a_response_is_returned_by_the_kernel";d:0.005;s:76:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_resolve_pipes_to_callbacks";d:0.005;s:77:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_matches_everything_to_lowercase";d:0.004;s:85:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_resolve_pipes_to_controller_actions";d:0.004;s:114:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_resolve_pipes_to_controller_actions_through_using_the_fluent_api";d:0.004;s:80:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_resolve_pipes_with_middlewares";d:0.005;s:111:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_resolve_grouped_pipes_and_pass_all_key_to_the_contained_pipes";d:0.005;s:127:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_add_fallback_pipes_to_handle_any_request_which_could_not_be_matched_otherwise";d:0.007;s:74:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_match_dynamic_parameters";d:0.004;s:140:"Mshule\LaravelPipes\Tests\PipeRequestTest::its_dynamic_pattern_match_checks_first_for_placeholder_name_in_request_then_for_default_cue_value";d:0.005;s:83:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_match_multiple_dynamic_parameters";d:0.007;s:89:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_match_dynamic_parameters_to_any_request";d:0.007;s:84:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_add_conditions_to_pipe_definitions";d:0.006;s:70:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_add_aliases_to_pipes";d:0.004;s:81:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_add_aliases_predefined_to_pipes";d:0.004;s:78:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_bind_models_to_pipe_requests";d:0.465;s:71:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_load_pipes_from_files";d:0.004;s:84:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_ignores_global_helper_functions_as_cue";d:0.004;s:128:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_can_identify_aliases_of_pipes_even_when_the_request_does_only_start_with_the_alias";d:0.004;s:123:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_doesnt_match_dynamic_parameters_if_the_request_contains_only_parts_of_the_cue";d:0.006;s:110:"Mshule\LaravelPipes\Tests\PipeRequestTest::it_does_not_match_one_character_to_a_pipe_if_it_is_no_dynamic_param";d:0.005;s:85:"Mshule\LaravelPipes\Tests\SubstituteBindingsTest::it_can_bind_models_to_pipe_requests";d:0.062;}}}
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | preset: laravel
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 7.1
5 | - 7.2
6 | - 7.3
7 | - 7.4
8 |
9 | env:
10 | global:
11 | - coverage=yes
12 | matrix:
13 | - setup=basic
14 | - setup=stable
15 | - setup=lowest
16 |
17 | before_install:
18 | - composer config discard-changes true
19 |
20 | before_script:
21 | - if [[ $setup = 'stable' ]]; then travis_retry composer update --prefer-dist --no-interaction --no-suggest --prefer-stable; fi
22 | - if [[ $setup = 'lowest' ]]; then travis_retry composer update --prefer-dist --no-interaction --no-suggest --prefer-lowest --prefer-stable; fi
23 |
24 | script:
25 | - if [[ $coverage = 'yes' ]]; then vendor/bin/phpunit -c phpunit.xml --coverage-clover=coverage.clover; fi
26 | - if [[ $coverage = 'no' ]]; then vendor/bin/phpunit -c phpunit.xml; fi
27 |
28 | after_script:
29 | - if [[ $coverage = 'yes' ]]; then wget https://scrutinizer-ci.com/ocular.phar; php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi
30 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## [v1.0.1](https://github.com/m-shule/laravel-pipes/tree/v1.0.1) (2019-05-22)
4 | [Full Changelog](https://github.com/m-shule/laravel-pipes/compare/v1.0...v1.0.1)
5 |
6 | **Merged pull requests:**
7 |
8 | - Fix Param matching [\#3](https://github.com/m-shule/laravel-pipes/pull/3) ([Naoray](https://github.com/Naoray))
9 | - Apply fixes from StyleCI [\#2](https://github.com/m-shule/laravel-pipes/pull/2) ([Naoray](https://github.com/Naoray))
10 | - Apply fixes from StyleCI [\#1](https://github.com/m-shule/laravel-pipes/pull/1) ([Naoray](https://github.com/Naoray))
11 |
12 | ## [v1.0](https://github.com/m-shule/laravel-pipes/tree/v1.0) (2019-05-20)
13 |
14 |
15 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are **welcome** and will be fully **credited**.
4 |
5 | We accept contributions via Pull Requests on [Github](https://github.com/mshule/laravel-pipes).
6 |
7 |
8 | ## Pull Requests
9 |
10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).
11 |
12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests.
13 |
14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
15 |
16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option.
17 |
18 | - **Create feature branches** - Don't ask us to pull from your master branch.
19 |
20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
21 |
22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
23 |
24 |
25 | ## Running Tests
26 |
27 | ``` bash
28 | $ composer test
29 | ```
30 |
31 |
32 | **Happy coding**!
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 M-Shule Ltd.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/clover.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mshule/laravel-pipes",
3 | "description": "A description for laravel-pipes.",
4 | "type": "package",
5 | "license": "MIT",
6 | "keywords": [
7 | "laravel"
8 | ],
9 | "authors": [
10 | {
11 | "name": "Krishan Koenig, Otieno Julie",
12 | "email": "krishan.koenig@googlemail.com"
13 | }
14 | ],
15 | "require": {
16 | "illuminate/support": "~5.7.0|~5.8.0|^6.0|^7.0|^8.0",
17 | "illuminate/http": "~5.7.0|~5.8.0|^6.0|^7.0|^8.0",
18 | "illuminate/routing": "~5.7.0|~5.8.0|^6.0|^7.0|^8.0"
19 | },
20 | "autoload": {
21 | "psr-4": {
22 | "Mshule\\LaravelPipes\\": "./src"
23 | }
24 | },
25 | "autoload-dev": {
26 | "psr-4": {
27 | "Mshule\\LaravelPipes\\Tests\\": "tests"
28 | }
29 | },
30 | "scripts": {
31 | "test": "vendor/bin/phpunit"
32 | },
33 | "extra": {
34 | "laravel": {
35 | "providers": [
36 | "Mshule\\LaravelPipes\\LaravelPipesServiceProvider"
37 | ],
38 | "aliases": {
39 | "Pipe": "Mshule\\LaravelPipes\\Facades\\Pipe"
40 | }
41 | }
42 | },
43 | "require-dev": {
44 | "orchestra/testbench": "~3.8.0|^4.0|^5.0|^6.0"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/config/pipes.php:
--------------------------------------------------------------------------------
1 | false,
18 |
19 | /*
20 | |--------------------------------------------------------------------------
21 | | Namespace
22 | |--------------------------------------------------------------------------
23 | |
24 | | This value determines the default namespace of your pipes. By default
25 | | it is set to `App\Pipes\Controllers`.
26 | |
27 | */
28 |
29 | 'namespace' => 'App\Pipes\Controllers',
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Incoming Request Path
34 | |--------------------------------------------------------------------------
35 | |
36 | | This value is the name of your request path for handling incoming
37 | | api requests which will then be dispatched through your pipes.
38 | | You only need this route as endpoint for your api provider.
39 | |
40 | */
41 |
42 | 'incoming_request_path' => 'handle-notification',
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Queue
47 | |--------------------------------------------------------------------------
48 | |
49 | | Sets the queue on which the job is run which boots the
50 | | akernel and starts the whole laravel-pipe lifecycle
51 | |
52 | */
53 |
54 | 'queue' => env('PIPE_QUEUE', 'default'),
55 | ];
56 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 | tests
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | src/
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # laravel-pipes
2 |
3 | [](LICENSE.md)
4 | [](https://travis-ci.org/m-shule/laravel-pipes)
5 | [](https://codecov.io/gh/m-shule/laravel-pipes)
6 | [](https://packagist.org/packages/mshule/laravel-pipes)
7 |
8 | Handling notifications from API's is simple when they only require to handle one type of notification, but if you have to handle multiple requests e.g. from an SMS API it can get messy. Similar to [Botman](https://botman.io)'s hear() method, this package provides a slightly different approach which could be used as part of another Botman like implementation. This package offers a similar API as you are used to from the [Laravel Routes](https://laravel.com/docs/5.8/routing).
9 |
10 | ## Install
11 |
12 | `composer require mshule/laravel-pipes`
13 |
14 | The incoming web request will be handled by `your_app_domain` + whatever you put in the `pipes.incoming_request_path` config. By default, the path will result in `your_app_domain/handle-notification`.
15 |
16 | **Optional: Create a separate route file for your pipes.**
17 |
18 | 1. add a new file `routes/pipes.php`
19 | 2. set the `pipes.load_routes_file` to `true`
20 |
21 | ## Usage
22 |
23 | To get an overview of all functionalities this package offers, you can check the `tests/PipeRequestTest.php`.
24 |
25 | ### Handling Pipes
26 |
27 | Pipes are matched by the keys and values of the request's data attributes.
28 |
29 | ```php
30 | // define pipe match for `foo` => `bar`
31 | // key, value, action
32 | Pipe::match('foo', 'bar', function () {
33 | return 'matched';
34 | });
35 |
36 | // same as
37 | Pipe::match('foo:bar', function () {
38 | return 'matched';
39 | })
40 |
41 | $this->pipe(['foo' => 'bar'])
42 | ->assertSee('matched'); // true
43 | ```
44 |
45 | Attributes can be bound dynamically to the pipe-request.
46 |
47 | ```php
48 | Pipe::match('foo:{bar}', function ($bar) {
49 | return $bar;
50 | });
51 |
52 | $this->pipe(['foo' => 'bar'])
53 | ->assertSee('bar'); // true
54 |
55 | $this->pipe(['foo' => 'other'])
56 | ->assertSee('other'); // true
57 | ```
58 |
59 | Instead of handling all pipe requests inside a callback, you can also redirect to a controller action.
60 |
61 | ```php
62 | Pipe::match('foo:{bar}', 'SomeController@index');
63 | ```
64 |
65 | If you want to handle multiple requests with different attribute keys you can use the `Pipe::any()` method.
66 |
67 | ```php
68 | Pipe::any('{bar}', 'SomeController@index');
69 | ```
70 |
71 | ### Other Options
72 |
73 | **alias()**
74 | Sometimes the user might have a typo in their message or you simply want to have different cues available to trigger a Pipe.
75 |
76 | ```php
77 | Pipe::any('bar', 'FooBarController')
78 | ->alias(['ba', 'b-r', 'bas']);
79 | ```
80 |
81 | The `FooBarController` will now be called upon `ba`, `b-r`, `bas` or as originally intended on `bar`.
82 |
83 | **namespace()**
84 | As you have probably noted the `routes/pipes.php` file is bound to a namespace configurable in the `config/pipes.php`. If you want to define a group with a different namespace, you can use the `namespace()` method:
85 |
86 | ```php
87 | Pipe::middleware('pipe')
88 | ->namespace(config('pipes.namespace'))
89 | ->group(function () {
90 | // define your namespaced pipes here
91 | });
92 | ```
93 |
94 | **key()**
95 | Like demonstrated in the first section of the *Handling Pipes* documentation, you can define Pipe routes in man different ways.
96 |
97 | ```php
98 | Pipe::match('foo', 'bar', function () {});
99 |
100 | // same as
101 | Pipe::match('foo:bar', function () {});
102 | ```
103 |
104 | There is a third option to specify the `key` of a Pipe by using the `key()` method.
105 |
106 | ```php
107 | Pipe::key('foo')->match('bar', function () {});
108 | ```
109 |
110 | The key method is handy if you have got several pipe routes which react to the same key.
111 |
112 | ```php
113 | Pipe::key('text')
114 | ->group(function () {
115 | // all pipe definitions within here will check for the `text` as key in the incoming request
116 | Pipe::match('some-text', function () {});
117 | });
118 | ```
119 |
120 | **where()**
121 | To further specify which request should be sent to a specific handler you can define conditions on each pipe like you are used to with [Laravel routes](https://laravel.com/docs/5.8/routing#parameters-regular-expression-constraints).
122 |
123 | ```php
124 | Pipe::any('{foo}', function ($foo) {
125 | return $foo;
126 | })->where('foo', 'bar');
127 |
128 | Pipe::any('{foo}', function ($foo) {
129 | return $foo;
130 | })->where('foo', '[a-z]+');
131 | ```
132 |
133 | **Understanding Pipe Life Cycle**
134 |
135 | The laravel-pipes lifecycle starts with a `post` request which is sent to the `pipes.incoming_request_path`. The `ExecutePipeRequest` Job is dispatched and an HTTP response returned - this is important since the pipe request is handled asynchronously if you have another queue driver than `sync`. In the Job, the `$request` is passed to the Pipe-Kernel's `handle()` method where it is passed through the global pipe-middlewares. The request is matched with the registered pipes and if a match is found the response is returned, otherwise a `NotFoundPipeException` is thrown.
136 |
137 | **Define the queue**
138 |
139 | As explained in the section above, a job is triggered to start the pipe-lifecycle. With the `pipes.queue` option you can define a seperate queue to run the pipe job on.
140 |
141 | **Testing Pipes**
142 |
143 | This package provides a simple trait to perform pipe requests. The `MakesPipeRequests` Trait provides a `pipe()` method to perform a pipe-request. The method fires a `post` request to the specified endpoint in `pipes.incoming_request_path`, but it is much easier to write `$this->pipe(...)` than `$this->post(config('pipes.incoming_request_path), [...])`.
144 |
145 | Since the pipe request is executed through a job, you have to use the `Pipe::fake()` method to get access to your responses.
146 |
147 | ```php
148 | Pipe::fake();
149 |
150 | $this->pipe(...);
151 |
152 | Pipe::assertResponded(function ($response) {
153 | $response->assertOk()
154 | ->assertSee(...);
155 | });
156 | ```
157 |
158 | Behind the scenes the `Pipe::fake()` method simply triggers the `Event::fake()` with the `IncomingPipeRequest` and `IncomingPipeResonse` events.
159 |
160 | ## Testing
161 |
162 | Run the tests with:
163 |
164 | ```bash
165 | vendor/bin/phpunit
166 | ```
167 |
168 | ## Changelog
169 |
170 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
171 |
172 | ## Contributing
173 |
174 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
175 |
176 | ## Security
177 |
178 | If you discover any security-related issues, please email DummyAuthorEmail instead of using the issue tracker.
179 |
180 | ## License
181 |
182 | The MIT License (MIT). Please see [License File](/LICENSE.md) for more information.
183 |
--------------------------------------------------------------------------------
/routes/api.php:
--------------------------------------------------------------------------------
1 | onQueue(config('pipes.queue'));
12 |
13 | return Pipe::response($request);
14 | });
15 |
--------------------------------------------------------------------------------
/src/Contracts/Registrar.php:
--------------------------------------------------------------------------------
1 | request = $request;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Events/IncomingPipeResponse.php:
--------------------------------------------------------------------------------
1 | response = $response;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Exceptions/NoKeysSpecifiedException.php:
--------------------------------------------------------------------------------
1 | method()} {$request->url()}", $previous, $headers, $code);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Facades/Pipe.php:
--------------------------------------------------------------------------------
1 | 0
24 | ? $eventsToFake
25 | : [IncomingPipeRequest::class, IncomingPipeResponse::class]
26 | );
27 |
28 | static::swap($fake = new PipeFake());
29 |
30 | return $fake;
31 | }
32 |
33 | /**
34 | * Get the registered name of the component.
35 | *
36 | * @return string
37 | */
38 | protected static function getFacadeAccessor()
39 | {
40 | return 'piper';
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Http/Middleware/SubstituteBindings.php:
--------------------------------------------------------------------------------
1 | pipe());
20 |
21 | Pipe::substituteImplicitBindings($pipe);
22 |
23 | return $next($request);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Jobs/ExecutePipeRequest.php:
--------------------------------------------------------------------------------
1 | data = $data;
32 | }
33 |
34 | /**
35 | * Execute the job.
36 | */
37 | public function handle()
38 | {
39 | $kernel = resolve(Kernel::class);
40 | $request = Request::reconstruct($this->data);
41 |
42 | event(new IncomingPipeRequest($request));
43 |
44 | $response = $kernel->handle($request);
45 |
46 | event(new IncomingPipeResponse($response));
47 |
48 | $kernel->terminate($request, $response);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Kernel.php:
--------------------------------------------------------------------------------
1 | [
30 | \Mshule\LaravelPipes\Http\Middleware\SubstituteBindings::class,
31 | ],
32 | ];
33 |
34 | /**
35 | * The application's route middleware.
36 | *
37 | * @var array
38 | */
39 | protected $routeMiddleware = [
40 | 'bindings' => \Mshule\LaravelPipes\Http\Middleware\SubstituteBindings::class,
41 | ];
42 |
43 | /**
44 | * The priority-sorted list of middleware.
45 | *
46 | * Forces non-global middleware to always be in the given order.
47 | *
48 | * @var array
49 | */
50 | protected $middlewarePriority = [
51 | \Mshule\LaravelPipes\Http\Middleware\SubstituteBindings::class,
52 | ];
53 |
54 | /**
55 | * Create a new PipeRequestHandler instance.
56 | *
57 | * @param \Illuminate\Contracts\Foundation\Application $app
58 | * @param \Mshule\LaravelPipes\Piper $piper
59 | */
60 | public function __construct(Application $app, Piper $piper)
61 | {
62 | $this->piper = $piper;
63 |
64 | parent::__construct($app, $piper);
65 | }
66 |
67 | /**
68 | * Handle an incoming HTTP request.
69 | *
70 | * @param \Mshule\LaravelPipes\Request $request
71 | *
72 | * @return \Illuminate\Http\Response
73 | */
74 | public function handle($request)
75 | {
76 | try {
77 | $response = $this->sendRequestThroughPipes($request);
78 | } catch (NotFoundPipeException $e) {
79 | throw new NotFoundPipeException($request);
80 | } catch (Exception $e) {
81 | $this->reportException($e);
82 |
83 | $response = $this->renderException($request, $e);
84 | } catch (Throwable $e) {
85 | $this->reportException($e = new FatalThrowableError($e));
86 |
87 | $response = $this->renderException($request, $e);
88 | }
89 |
90 | return Response::from($response);
91 | }
92 |
93 | /**
94 | * Send the given request through the middleware / pipes.
95 | *
96 | * @param \Illuminate\Http\Request $request
97 | *
98 | * @return \Illuminate\Http\Response
99 | */
100 | protected function sendRequestThroughPipes($request)
101 | {
102 | $this->app->instance('request', $request);
103 |
104 | Facade::clearResolvedInstance('request');
105 |
106 | $this->bootstrap();
107 |
108 | return (new Pipeline($this->app))
109 | ->send($request)
110 | ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
111 | ->then($this->dispatchToPiper());
112 | }
113 |
114 | /**
115 | * Get the pipe dispatcher callback.
116 | *
117 | * @return \Closure
118 | */
119 | protected function dispatchToPiper()
120 | {
121 | return function ($request) {
122 | $this->app->instance('request', $request);
123 |
124 | return $this->piper->dispatch($request);
125 | };
126 | }
127 |
128 | /**
129 | * Gather the pipe middleware for the given request.
130 | *
131 | * @param \Illuminate\Http\Request $request
132 | *
133 | * @return array
134 | */
135 | protected function gatherRouteMiddleware($request)
136 | {
137 | if ($pipe = $request->route()) {
138 | return $this->piper->gatherRouteMiddleware($pipe);
139 | }
140 |
141 | return [];
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/LaravelPipesServiceProvider.php:
--------------------------------------------------------------------------------
1 | performBindings();
16 |
17 | $this->publishes([
18 | __DIR__.'/../config/pipes.php' => config_path('pipes.php'),
19 | ]);
20 | }
21 |
22 | /**
23 | * Perform all needed bindings.
24 | */
25 | protected function performBindings()
26 | {
27 | $this->app->singleton('piper', function ($app) {
28 | return new Piper($app);
29 | });
30 |
31 | $this->app->singleton(Kernel::class, function ($app) {
32 | return new Kernel($app, resolve('piper'));
33 | });
34 |
35 | $this->app->alias('piper', \Mshule\LaravelPipes\Piper::class);
36 |
37 | $this->app->bind('pipe_any', function () {
38 | return '*any*';
39 | });
40 | }
41 |
42 | /**
43 | * Bootstrap services.
44 | */
45 | public function boot()
46 | {
47 | $this->mergeConfigFrom(
48 | __DIR__.'/../config/pipes.php',
49 | 'pipes'
50 | );
51 |
52 | $this->loadRoutes();
53 | }
54 |
55 | /**
56 | * Load all routes for using pipes.
57 | */
58 | protected function loadRoutes()
59 | {
60 | $this->loadRoutesFrom(__DIR__.'/../routes/api.php');
61 |
62 | if (config('pipes.load_routes_file')) {
63 | $this->mapPipeRoutes();
64 | }
65 | }
66 |
67 | /**
68 | * Define the "pipe" routes for the application.
69 | *
70 | * These routes are typically stateless.
71 | */
72 | protected function mapPipeRoutes()
73 | {
74 | Pipe::middleware('pipe')
75 | ->namespace(config('pipes.namespace'))
76 | ->group(base_path('routes/pipes.php'));
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/Matching/CueValidator.php:
--------------------------------------------------------------------------------
1 | cueStartsWithPlaceholder()) {
25 | return true;
26 | }
27 |
28 | $any = resolve('pipe_any');
29 | $keys = $pipe->key() === $any ? $request->all() : $request->only($pipe->key());
30 | $values = array_map('strtolower', array_values($keys));
31 |
32 | array_push($values, $any);
33 |
34 | $matched = collect($values)->contains(function ($value) use ($pipe) {
35 | if (! $pipe->cueContainsPlaceholder()) {
36 | return Str::startsWith($value, $pipe->cue());
37 | }
38 |
39 | if (! $pipe->cueStartsWithPlaceholder()) {
40 | return Str::startsWith($value, trim(Str::before($pipe->cue(), '{')));
41 | }
42 |
43 | // to be able to match starting strings with $cue and including a
44 | // param `trigger {param}` inside the cue we will figure out
45 | // which string is longer and use this for our truth test.
46 | [$haystack, $needle] = strlen($value) >= strlen($pipe->cue())
47 | ? [$value, $pipe->cue()]
48 | : [$pipe->cue(), $value];
49 |
50 | return Str::startsWith($haystack, $needle);
51 | });
52 |
53 | if ($matched || ! $pipe->hasAlias()) {
54 | return $matched;
55 | }
56 |
57 | // if a pipe has alias specified we will check whether
58 | // the alias and request values have a common subset
59 | return collect($values)->contains(function ($value) use ($pipe) {
60 | return $pipe->hasAlias($value);
61 | });
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Matching/KeyValidator.php:
--------------------------------------------------------------------------------
1 | keys();
21 |
22 | array_push($keys, resolve('pipe_any'));
23 |
24 | return in_array($pipe->key(), $keys);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Matching/PatternValidator.php:
--------------------------------------------------------------------------------
1 | compiled->getRegex(), $pipe->path($request));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Matching/ValidatorInterface.php:
--------------------------------------------------------------------------------
1 | cue = strtolower($cue);
63 | $this->key = strtolower($key);
64 | $this->alias = Arr::get($this->action, 'alias', []);
65 | }
66 |
67 | /**
68 | * Parse the route action into a standard array.
69 | *
70 | * @param callable|array|null $action
71 | *
72 | * @throws \UnexpectedValueException
73 | *
74 | * @return array
75 | */
76 | protected function parseAction($action)
77 | {
78 | return PipeAction::parse($this->cue, $action);
79 | }
80 |
81 | /**
82 | * Set the piper instance on the pipe.
83 | *
84 | * @param \Mshule\LaravelPipes\Piper $piper
85 | *
86 | * @return $this
87 | */
88 | public function setPiper(Piper $piper)
89 | {
90 | $this->piper = $piper;
91 |
92 | return $this;
93 | }
94 |
95 | /**
96 | * Get the cue associated with the pipe.
97 | *
98 | * @return string
99 | */
100 | public function cue()
101 | {
102 | return $this->cue;
103 | }
104 |
105 | /**
106 | * Get the key the pipe responds to.
107 | *
108 | * @return array
109 | */
110 | public function key()
111 | {
112 | if ('placeholder' === $this->key) {
113 | $this->key = null;
114 | }
115 |
116 | if ($this->key) {
117 | return $this->key;
118 | }
119 |
120 | $key = $this->key ?? Arr::get($this->action, 'key', null);
121 |
122 | if (! $key) {
123 | throw new NoKeysSpecifiedException("No key were defined for {$this->pipe->cue()}");
124 | }
125 |
126 | return $this->key = strtolower($key);
127 | }
128 |
129 | /**
130 | * Bind the pipe to a given request for execution.
131 | *
132 | * @param \Illuminate\Http\Request $request
133 | *
134 | * @return $this
135 | */
136 | public function bind(Request $request)
137 | {
138 | $this->compileRoute();
139 |
140 | $this->parameters = (new PipeParameterBinder($this))
141 | ->parameters($request);
142 |
143 | $this->originalParameters = $this->parameters;
144 |
145 | return $this;
146 | }
147 |
148 | /**
149 | * Get the pipe validators for the instance.
150 | *
151 | * @return array
152 | */
153 | public static function getValidators()
154 | {
155 | if (isset(static::$validators)) {
156 | return static::$validators;
157 | }
158 |
159 | // To match the pipe, we will use a chain of responsibility pattern with the
160 | // validator implementations. We will spin through each one making sure it
161 | // passes and then we will know if the pipe as a whole matches request.
162 | return static::$validators = [
163 | new KeyValidator(), new CueValidator(), new PatternValidator(),
164 | ];
165 | }
166 |
167 | /**
168 | * Get the full path for the pipe with the replaced attributes from the request.
169 | *
170 | * @param Request $request
171 | */
172 | public function path(Request $request)
173 | {
174 | $replacements = collect($this->parameterNames())->map(function ($param) use ($request) {
175 | return $request->{$this->paramKey($param)};
176 | })->toArray();
177 |
178 | $path = preg_replace_array('/\\{[a-zA-Z]+\\}/', $replacements, $this->uri());
179 |
180 | return Str::startsWith($path, '/') ? $path : '/'.$path;
181 | }
182 |
183 | /**
184 | * Set a regular expression requirement on the route.
185 | *
186 | * @param array|string $name
187 | *
188 | * @return $this
189 | */
190 | public function alias($name)
191 | {
192 | foreach ($this->parseAlias($name) as $name) {
193 | $this->alias[] = strtolower($name);
194 | }
195 |
196 | return $this;
197 | }
198 |
199 | /**
200 | * Parse arguments to the alias method into an array.
201 | *
202 | * @param array|string $name
203 | * @param string $expression
204 | *
205 | * @return array
206 | */
207 | protected function parseAlias($name)
208 | {
209 | return is_array($name) ? $name : [$name];
210 | }
211 |
212 | /**
213 | * Checks if a pipe does have alias specified.
214 | *
215 | * @param string|null $key
216 | *
217 | * @return bool
218 | */
219 | public function hasAlias($key = null)
220 | {
221 | if (is_null($key)) {
222 | return (bool) count($this->getAlias());
223 | }
224 |
225 | return collect($this->getAlias())->contains(function ($alias) use ($key) {
226 | return Str::startsWith($key, $alias);
227 | });
228 | }
229 |
230 | /**
231 | * Get all alias of a pipe.
232 | *
233 | * @return array
234 | */
235 | public function getAlias()
236 | {
237 | return $this->alias;
238 | }
239 |
240 | /**
241 | * Checks if cue of this pipe starts with a placeholder.
242 | *
243 | * @return bool
244 | */
245 | public function cueStartsWithPlaceholder()
246 | {
247 | return Str::startsWith($this->cue(), '{');
248 | }
249 |
250 | /**
251 | * Checks if the pipes cue contains placeholders.
252 | *
253 | * @return bool
254 | */
255 | public function cueContainsPlaceholder()
256 | {
257 | return Str::contains($this->cue(), ['{', '}']);
258 | }
259 |
260 | /**
261 | * Checks if cue only contains one placeholder.
262 | *
263 | * @return bool
264 | */
265 | public function cueIsPlaceholder()
266 | {
267 | return preg_match('/^\\{[A-Za-z]+\\}$/', $this->cue());
268 | }
269 |
270 | /**
271 | * Get param key for the path matching of a pipe.
272 | *
273 | * @return string
274 | */
275 | public function paramKey($param)
276 | {
277 | if ($this->key() === resolve('pipe_any')) {
278 | return $param;
279 | }
280 |
281 | return $this->cueIsPlaceholder()
282 | ? $this->key()
283 | : $param ?? $this->key();
284 | }
285 | }
286 |
--------------------------------------------------------------------------------
/src/PipeAction.php:
--------------------------------------------------------------------------------
1 | function () use ($cue) {
21 | throw new LogicException("Pipe for [{$cue}] has no action.");
22 | }];
23 | }
24 |
25 | /**
26 | * Make an action for an invokable controller.
27 | *
28 | * @param string $action
29 | *
30 | * @throws \UnexpectedValueException
31 | *
32 | * @return string
33 | */
34 | protected static function makeInvokable($action)
35 | {
36 | if (! method_exists($action, '__invoke')) {
37 | throw new UnexpectedValueException("Invalid pipe action: [{$action}].");
38 | }
39 |
40 | return $action.'@__invoke';
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/PipeCollection.php:
--------------------------------------------------------------------------------
1 | addToCollections($pipe);
41 |
42 | return $pipe;
43 | }
44 |
45 | /**
46 | * Add the given pipe to the arrays of pipes.
47 | *
48 | * @param \Mshule\LaravelPipes\Pipe $pipe
49 | */
50 | protected function addToCollections($pipe)
51 | {
52 | $cue = $pipe->cue();
53 | $key = $pipe->key();
54 |
55 | $this->pipes[$key][$cue] = $pipe;
56 | $this->allPipes[$key.$cue] = $pipe;
57 | }
58 |
59 | /**
60 | * Find the first pipe matching a given request.
61 | *
62 | * @param \Illuminate\Http\Request $request
63 | * @param \Mshule\LaravelPipes\Pipe $pipe
64 | *
65 | * @throws Mshule\LaravelPipes\Exceptions\NotFoundPipeException
66 | */
67 | public function match(Request $request)
68 | {
69 | $pipes = $this->get($request->keys());
70 |
71 | // First, we will see if we can find a matching pipe for this current request
72 | // method. If we can, great, we can just return it so that it can be called
73 | // by the consumer.
74 | $pipe = $this->matchAgainstPipes($pipes, $request);
75 |
76 | if (! is_null($pipe)) {
77 | return $pipe->bind($request);
78 | }
79 |
80 | throw new NotFoundPipeException($request);
81 | }
82 |
83 | /**
84 | * Get pipes from the collection by attribute.
85 | *
86 | * @param string|null $
87 | *
88 | * @return array
89 | */
90 | public function get($keys = [])
91 | {
92 | if (0 === count($keys)) {
93 | return $this->getPipes();
94 | }
95 |
96 | array_push($keys, resolve('pipe_any'));
97 |
98 | return collect($keys)->map(function ($key) {
99 | return Arr::get($this->pipes, $key, []);
100 | })
101 | ->flatten()
102 | ->toArray();
103 | }
104 |
105 | /**
106 | * Determine if a pipe in the array matches the request.
107 | *
108 | * @param array $pipes
109 | * @param \Illuminate\Http\Request $request
110 | *
111 | * @return \Mshule\LaravelPipes\Pipe $pipe|null
112 | */
113 | protected function matchAgainstPipes(array $pipes, $request)
114 | {
115 | [$fallbacks, $pipes] = collect($pipes)->partition(function ($route) {
116 | return $route->isFallback;
117 | });
118 |
119 | return $pipes->merge($fallbacks)->first(function ($value) use ($request) {
120 | return $this->fetchMatchedPipes($value, $request);
121 | });
122 | }
123 |
124 | protected function fetchMatchedPipes($value, $request)
125 | {
126 | if (!$value->matches($request)) {
127 | foreach ([new KeyValidator(), new CueValidator(), new PatternValidator()] as $validator) {
128 | if (! $validator->matches($value, $request)) {
129 | return false;
130 | }
131 | }
132 | }
133 |
134 | return true;
135 | }
136 |
137 | /**
138 | * Get all of the pipes in the collection.
139 | *
140 | * @return array
141 | */
142 | public function getPipes()
143 | {
144 | return array_values($this->allPipes);
145 | }
146 |
147 | /**
148 | * Get an iterator for the items.
149 | *
150 | * @return \ArrayIterator
151 | */
152 | public function getIterator()
153 | {
154 | return new ArrayIterator($this->getPipes());
155 | }
156 |
157 | /**
158 | * Count the number of items in the collection.
159 | *
160 | * @return int
161 | */
162 | public function count()
163 | {
164 | return count($this->getPipes());
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/PipeGroup.php:
--------------------------------------------------------------------------------
1 | static::formatNamespace($new, $old),
22 | 'key' => static::formatKey($new, $old),
23 | 'where' => static::formatWhere($new, $old),
24 | ]);
25 |
26 | return array_merge_recursive(Arr::except(
27 | $old,
28 | ['namespace', 'key', 'where']
29 | ), $new);
30 | }
31 |
32 | /**
33 | * Format the attributes of the new group attributes.
34 | *
35 | * @param array $new
36 | * @param array $old
37 | *
38 | * @return string|null
39 | */
40 | public static function formatKey($new, $old)
41 | {
42 | return isset($new['key'])
43 | ? $new['key']
44 | : ($old['key'] ?? null);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/PipeParameterBinder.php:
--------------------------------------------------------------------------------
1 | route->compiled->getRegex(), $this->route->path($request), $matches);
18 |
19 | return $this->matchToKeys(array_slice($matches, 1));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/PipeRegistrar.php:
--------------------------------------------------------------------------------
1 | piper = $piper;
44 | }
45 |
46 | /**
47 | * Set the value for a given attribute.
48 | *
49 | * @param string $key
50 | * @param mixed $value
51 | *
52 | * @throws \InvalidArgumentException
53 | *
54 | * @return $this
55 | */
56 | public function attribute($key, $value)
57 | {
58 | if (! in_array($key, $this->allowedAttributes)) {
59 | throw new InvalidArgumentException("Attribute [{$key}] does not exist.");
60 | }
61 |
62 | $this->attributes[$key] = $value;
63 |
64 | return $this;
65 | }
66 |
67 | /**
68 | * Create a route group with shared attributes.
69 | *
70 | * @param \Closure|string $callback
71 | */
72 | public function group($callback)
73 | {
74 | $this->piper->group($this->attributes, $callback);
75 | }
76 |
77 | /**
78 | * Register a new pipe with the given verbs.
79 | *
80 | * @param string $inputs
81 | * @param string $cue
82 | * @param \Closure|array|string|callable $action
83 | *
84 | * @return \Mshule\LaravelPipes\Pipe
85 | */
86 | public function match($inputs, $cue, $action = null)
87 | {
88 | return $this->piper->match($inputs, $cue, $this->compileAction($action));
89 | }
90 |
91 | /**
92 | * Register a new pipe with the piper.
93 | *
94 | * @param string $method
95 | * @param string $uri
96 | * @param \Closure|array|string|null $action
97 | *
98 | * @return \Mshule\LaravelPipes\Pipe
99 | */
100 | protected function registerPipe($method, $cue, $action = null)
101 | {
102 | if (! is_array($action)) {
103 | $action = array_merge($this->attributes, $action ? ['uses' => $action] : []);
104 | }
105 |
106 | return $this->piper->{$method}($cue, $this->compileAction($action));
107 | }
108 |
109 | /**
110 | * Compile the action into an array including the attributes.
111 | *
112 | * @param \Closure|array|string|null $action
113 | *
114 | * @return array
115 | */
116 | protected function compileAction($action)
117 | {
118 | if (is_null($action)) {
119 | return $this->attributes;
120 | }
121 |
122 | if (is_string($action) || $action instanceof Closure) {
123 | $action = ['uses' => $action];
124 | }
125 |
126 | return array_merge($this->attributes, $action);
127 | }
128 |
129 | /**
130 | * Dynamically handle calls into the route registrar.
131 | *
132 | * @param string $method
133 | * @param array $parameters
134 | *
135 | * @throws \BadMethodCallException
136 | *
137 | * @return \Mshule\LaravelPipes\Pipe|$this
138 | */
139 | public function __call($method, $parameters)
140 | {
141 | if (in_array($method, $this->passthru)) {
142 | return $this->registerPipe($method, ...$parameters);
143 | }
144 |
145 | if (in_array($method, $this->allowedAttributes)) {
146 | if ('middleware' === $method) {
147 | return $this->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
148 | }
149 |
150 | return $this->attribute($method, $parameters[0]);
151 | }
152 |
153 | throw new BadMethodCallException(sprintf(
154 | 'Method %s::%s does not exist.',
155 | static::class,
156 | $method
157 | ));
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/Piper.php:
--------------------------------------------------------------------------------
1 | pipes = new PipeCollection();
34 | $this->container = $container ?: new Container();
35 | }
36 |
37 | /**
38 | * Register a new pipe with the given verbs.
39 | *
40 | * @param string $inputs
41 | * @param string $cue
42 | * @param \Closure|array|string|callable $action
43 | *
44 | * @return \Mshule\LaravelPipes\Pipe
45 | */
46 | public function any($cue, $action = [])
47 | {
48 | return $this->addPipe(resolve('pipe_any'), $cue, $action);
49 | }
50 |
51 | /**
52 | * Register a new Fallback pipe with the piper.
53 | *
54 | * @param \Closure|array|string|callable|null $action
55 | *
56 | * @return \Mshule\LaravelPipes\Pipe
57 | */
58 | public function fallback($action)
59 | {
60 | $placeholder = 'fallbackPlaceholder';
61 |
62 | return $this->addPipe(
63 | resolve('pipe_any'),
64 | "{{$placeholder}}",
65 | $action
66 | )->where($placeholder, '.*')->fallback();
67 | }
68 |
69 | /**
70 | * Register a new pipe with the given verbs.
71 | *
72 | * @param string $key
73 | * @param string $cue
74 | * @param \Closure|array|string|callable $action
75 | *
76 | * @return \Mshule\LaravelPipes\Pipe
77 | */
78 | public function match($key, $cue, $action = [])
79 | {
80 | // If only two arguments were entered and the first
81 | // does not contain a colon (:), we assume the
82 | // user either wants to allow any input or
83 | // will specify a specific input later on
84 | if (2 === count(func_get_args()) && (is_string($key) && ! Str::contains($key, ':'))) {
85 | return $this->addPipe('placeholder', $key, $cue);
86 | }
87 |
88 | return $this->addPipe($key, $cue, $action);
89 | }
90 |
91 | /**
92 | * Merge the given array with the last group stack.
93 | *
94 | * @param array $new
95 | *
96 | * @return array
97 | */
98 | public function mergeWithLastGroup($new, $prependExistingPrefix = true)
99 | {
100 | return PipeGroup::merge($new, end($this->groupStack), $prependExistingPrefix);
101 | }
102 |
103 | /**
104 | * Add a pipe to the underlying pipe collection.
105 | *
106 | * @param string $inputs
107 | * @param string $cue
108 | * @param \Closure|array|string|callable $action
109 | *
110 | * @return \Mshule\LaravelPipes\Pipe
111 | */
112 | public function addPipe($key, $cue, $action = [])
113 | {
114 | return $this->pipes->add($this->createPipe($key, $cue, $action));
115 | }
116 |
117 | /**
118 | * Add a route to the underlying route collection.
119 | *
120 | * @param array|string $methods
121 | * @param string $uri
122 | * @param \Closure|array|string|callable|null $action
123 | */
124 | public function addRoute($methods, $uri, $action)
125 | {
126 | $this->handleNotIntendedMethods();
127 | }
128 |
129 | /**
130 | * Create a new pipe instance.
131 | *
132 | * @param string $key
133 | * @param string $cue
134 | * @param mixed $action
135 | *
136 | * @return \Mshule\LaravelPipes\Pipe
137 | */
138 | protected function createPipe($key, $cue, $action = [])
139 | {
140 | // if the input was passed in combination with the cue
141 | // seperated by a colon (:), the values need to
142 | // be reassigned to the right variable.
143 | if (is_string($key) && Str::contains($key, ':')) {
144 | [$key, $cue, $action] = array_merge(
145 | explode(':', $key),
146 | [array_merge(['uses' => $cue], $action)]
147 | );
148 | }
149 |
150 | // if the input was passed through the fluent api the
151 | // order of the func argument have to be rearranged.
152 | if (($cue instanceof Closure && is_callable($cue)) || Str::contains($cue, '@')) {
153 | [$key, $cue, $action] = [$action, $key, $cue];
154 | }
155 |
156 | // If the pipe is pointing to a controller we will parse the pipe action into
157 | // an acceptable array format before registering it and creating this pipe
158 | // instance itself. We need to build the Closure that will call this out.
159 | if ($this->actionReferencesController($action)) {
160 | $action = $this->convertToControllerAction($action);
161 | }
162 |
163 | $pipe = $this->newPipe(
164 | $cue,
165 | $action,
166 | $key
167 | );
168 |
169 | // If we have groups that need to be merged, we will merge them now after this
170 | // pipe has already been created and is ready to go. After we're done with
171 | // the merge we will be ready to return the pipe back out to the caller.
172 | if ($this->hasGroupStack()) {
173 | $this->mergeGroupAttributesIntoRoute($pipe);
174 | }
175 |
176 | $this->addWhereClausesToRoute($pipe);
177 |
178 | return $pipe;
179 | }
180 |
181 | /**
182 | * Create a new Pipe object.
183 | *
184 | * @param string $cue
185 | * @param mixed $action
186 | * @param array|string $key
187 | *
188 | * @return \Mshule\LaravelPipes\Pipe
189 | */
190 | protected function newPipe($cue, $action, $key = '')
191 | {
192 | return (new Pipe($cue, $action, $key))
193 | ->setPiper($this)
194 | ->setContainer($this->container);
195 | }
196 |
197 | /**
198 | * Dispatch the request to a pipe and return the response.
199 | *
200 | * @param \Illuminate\Http\Request $request
201 | *
202 | * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
203 | */
204 | public function dispatchToRoute(Request $request)
205 | {
206 | return $this->runPipe($request, $this->findPipe($request));
207 | }
208 |
209 | /**
210 | * Find the pipe matching a given request.
211 | *
212 | * @param \Illuminate\Http\Request $request
213 | * @param \Mshule\LaravelPipes\Pipe $pipe
214 | */
215 | protected function findPipe($request)
216 | {
217 | $this->current = $pipe = $this->pipes->match($request);
218 |
219 | return $pipe;
220 | }
221 |
222 | /**
223 | * Return the response for the given pipe.
224 | *
225 | * @param \Illuminate\Http\Request $request
226 | * @param \Mshule\LaravelPipes\Pipe $pipe
227 | *
228 | * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
229 | */
230 | protected function runPipe(Request $request, Pipe $pipe)
231 | {
232 | $request->setPipeResolver(function () use ($pipe) {
233 | return $pipe;
234 | });
235 |
236 | return $this->prepareResponse(
237 | $request,
238 | $this->runRouteWithinStack($pipe, $request)
239 | );
240 | }
241 |
242 | /**
243 | * Get the response resolver callback.
244 | *
245 | * @return \Closure
246 | */
247 | public function getResponseResolver()
248 | {
249 | return $this->responseResolver ?: function ($request) {
250 | return response('ok');
251 | };
252 | }
253 |
254 | /**
255 | * Set the response resolver callback.
256 | *
257 | * @param \Closure $callback
258 | *
259 | * @return $this
260 | */
261 | public function setResponseResolver(Closure $callback)
262 | {
263 | $this->responseResolver = $callback;
264 |
265 | return $this;
266 | }
267 |
268 | /**
269 | * Return standard response.
270 | *
271 | * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
272 | */
273 | public function response($request)
274 | {
275 | return call_user_func($this->getResponseResolver(), $request);
276 | }
277 |
278 | /**
279 | * Throws exceptions to notify user of methods not allowed to use in a pipe context.
280 | *
281 | * @throws Exception
282 | */
283 | private function handleNotIntendedMethods()
284 | {
285 | throw new Exception('The methods solely used by the router instance are not intended to be used in a pipe context!');
286 | }
287 |
288 | /**
289 | * Dynamically handle calls into the piper instance.
290 | *
291 | * @param string $method
292 | * @param array $parameters
293 | *
294 | * @return mixed
295 | */
296 | public function __call($method, $parameters)
297 | {
298 | if (static::hasMacro($method)) {
299 | return $this->macroCall($method, $parameters);
300 | }
301 |
302 | if ('middleware' === $method) {
303 | return (new PipeRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
304 | }
305 |
306 | return (new PipeRegistrar($this))->attribute($method, $parameters[0]);
307 | }
308 | }
309 |
--------------------------------------------------------------------------------
/src/Request.php:
--------------------------------------------------------------------------------
1 | query(),
28 | $request->post(),
29 | $request->input(),
30 | $request->cookie(),
31 | $request->file(),
32 | $request->server(),
33 | $request->getContent(),
34 | ];
35 | }
36 |
37 | /**
38 | * Reconstruct request from array.
39 | *
40 | * @param array $data
41 | *
42 | * @return self
43 | */
44 | public static function reconstruct($data, $request = self::class)
45 | {
46 | return tap(resolve($request), function ($request) use ($data) {
47 | $request->initialize(...$data);
48 | });
49 | }
50 |
51 | /**
52 | * Get the pipe resolver callback.
53 | *
54 | * @return \Closure
55 | */
56 | public function getPipeResolver()
57 | {
58 | return $this->pipeResolver ?: function () {
59 | };
60 | }
61 |
62 | /**
63 | * Set the Pipe resolver callback.
64 | *
65 | * @param \Closure $callback
66 | *
67 | * @return $this
68 | */
69 | public function setPipeResolver(Closure $callback)
70 | {
71 | $this->pipeResolver = $callback;
72 |
73 | return $this;
74 | }
75 |
76 | /**
77 | * Get the pipe handling the request.
78 | *
79 | * @param string|null $param
80 | * @param mixed $default
81 | *
82 | * @return \Mshule\LaravelPipes\Pipe|object|string
83 | */
84 | public function pipe($param = null, $default = null)
85 | {
86 | $pipe = call_user_func($this->getPipeResolver());
87 |
88 | if (is_null($pipe) || is_null($param)) {
89 | return $pipe;
90 | }
91 |
92 | return $pipe->parameter($param, $default);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Response.php:
--------------------------------------------------------------------------------
1 | environment('testing')) {
19 | return $response;
20 | }
21 |
22 | if (version_compare(Application::VERSION, '7.0.0', '>=')) {
23 | return new \Illuminate\Testing\TestResponse($response);
24 | }
25 |
26 | return new \Illuminate\Foundation\Testing\TestResponse($response);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Testing/Fakes/PipeFake.php:
--------------------------------------------------------------------------------
1 | getTruthTestCallback($callback, 'request');
20 |
21 | Event::assertDispatched(IncomingPipeRequest::class, $truthTestCallback);
22 | }
23 |
24 | /**
25 | * Assert if a response was dispatched based on a truth-test callback.
26 | *
27 | * @param \Closure $callback
28 | */
29 | public function assertResponded($callback = null)
30 | {
31 | $truthTestCallback = $this->getTruthTestCallback($callback, 'response');
32 |
33 | Event::assertDispatched(IncomingPipeResponse::class, $truthTestCallback);
34 | }
35 |
36 | /**
37 | * Get truth test callback.
38 | *
39 | * @param \Closure|null $callback
40 | * @param string|null $property
41 | *
42 | * @return \Closure
43 | */
44 | protected function getTruthTestCallback($callback = null, $property = null)
45 | {
46 | return is_callable($callback)
47 | ? function ($event) use ($callback, $property) {
48 | $callback($event->{$property});
49 |
50 | return true;
51 | }
52 | : null;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Testing/MakesPipeRequests.php:
--------------------------------------------------------------------------------
1 | post(config('pipes.incoming_request_path'), $data);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tests/Fixtures/Controllers/TestController.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
18 | $table->string('name');
19 | $table->timestamp('done_at')->nullable();
20 | $table->timestamps();
21 | });
22 | }
23 |
24 | /**
25 | * Reverse the migrations.
26 | *
27 | * @return void
28 | */
29 | public function down()
30 | {
31 | Schema::dropIfExists('todos');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tests/Fixtures/Models/Todo.php:
--------------------------------------------------------------------------------
1 | withoutExceptionHandling();
19 | $this->expectException(NotFoundPipeException::class);
20 |
21 | $this->pipe();
22 | }
23 |
24 | /** @test */
25 | public function it_returns_a_ok_200er_response()
26 | {
27 | $this->pipe()
28 | ->assertOk()
29 | ->assertSee('ok');
30 | }
31 |
32 | /** @test */
33 | public function the_response_can_be_changed_through_the_response_resolver()
34 | {
35 | Pipe::setResponseResolver(function ($request) {
36 | return $request->message;
37 | });
38 |
39 | $this->pipe(['message' => 'test'])
40 | ->assertOk()
41 | ->assertSee('test');
42 | }
43 |
44 | /** @test */
45 | public function it_fires_an_incoming_pipe_request_event_when_a_request_is_handled_by_the_kernel()
46 | {
47 | Pipe::fake();
48 |
49 | $this->pipe();
50 |
51 | Pipe::assertRequested();
52 | }
53 |
54 | /** @test */
55 | public function it_fires_an_incoming_pipe_response_event_when_a_response_is_returned_by_the_kernel()
56 | {
57 | Pipe::fake();
58 |
59 | $this->pipe();
60 |
61 | Pipe::assertResponded();
62 | }
63 |
64 | /** @test */
65 | public function it_can_resolve_pipes_to_callbacks()
66 | {
67 | Pipe::fake();
68 |
69 | Pipe::match('text', 'test', function () {
70 | return response('pipe was resolved', 200);
71 | });
72 |
73 | $this->pipe(['text' => 'test']);
74 |
75 | Pipe::assertResponded(function ($response) {
76 | $response->assertOk()
77 | ->assertSee('pipe was resolved');
78 | });
79 | }
80 |
81 | /** @test */
82 | public function it_matches_everything_to_lowercase()
83 | {
84 | Pipe::fake();
85 |
86 | Pipe::match('text', 'test', function () {
87 | return response('pipe was resolved', 200);
88 | });
89 |
90 | $this->pipe(['text' => 'TEST']);
91 |
92 | Pipe::assertResponded(function ($response) {
93 | $response->assertOk()
94 | ->assertSee('pipe was resolved');
95 | });
96 | }
97 |
98 | /** @test */
99 | public function it_can_resolve_pipes_to_controller_actions()
100 | {
101 | Pipe::fake();
102 |
103 | Pipe::match('text', 'something', '\Mshule\LaravelPipes\Tests\Fixtures\Controllers\TestController@doSomething');
104 |
105 | $this->pipe([
106 | 'text' => 'something',
107 | ]);
108 |
109 | Pipe::assertResponded(function ($response) {
110 | $response->assertOk()
111 | ->assertSee('did something');
112 | });
113 | }
114 |
115 | /** @test */
116 | public function it_can_resolve_pipes_to_controller_actions_through_using_the_fluent_api()
117 | {
118 | Pipe::fake();
119 |
120 | Pipe::match('text:something', '\Mshule\LaravelPipes\Tests\Fixtures\Controllers\TestController@doSomething');
121 |
122 | $this->pipe([
123 | 'text' => 'something',
124 | ]);
125 |
126 | Pipe::assertResponded(function ($response) {
127 | $response->assertOk()
128 | ->assertSee('did something');
129 | });
130 | }
131 |
132 | /** @test */
133 | public function it_can_resolve_pipes_with_middlewares()
134 | {
135 | Pipe::fake();
136 |
137 | Pipe::middleware(function ($request, $next) {
138 | return 'middleware succeeded';
139 | })->match('text', 'middle', function () {
140 | return 'middleware failed';
141 | });
142 |
143 | $this->pipe([
144 | 'text' => 'middle',
145 | ]);
146 |
147 | Pipe::assertResponded(function ($response) {
148 | $response->assertOk()
149 | ->assertSee('middleware succeeded');
150 | });
151 | }
152 |
153 | /** @test */
154 | public function it_can_resolve_grouped_pipes_and_pass_all_key_to_the_contained_pipes()
155 | {
156 | Pipe::fake();
157 |
158 | Pipe::key('text')->group(function () {
159 | Pipe::match('something', function () {
160 | return 'did one';
161 | });
162 | });
163 |
164 | $this->pipe([
165 | 'text' => 'something',
166 | ]);
167 |
168 | Pipe::assertResponded(function ($response) {
169 | $response->assertOk()
170 | ->assertSee('did one');
171 | });
172 | }
173 |
174 | /** @test */
175 | public function it_can_add_fallback_pipes_to_handle_any_request_which_could_not_be_matched_otherwise()
176 | {
177 | Pipe::fake();
178 |
179 | Pipe::fallback(function () {
180 | return 'no other pipe did match up';
181 | });
182 |
183 | $this->pipe(['foo' => 'bar']);
184 |
185 | Pipe::assertResponded(function ($response) {
186 | $response->assertOk()
187 | ->assertSee('no other pipe did match up');
188 | });
189 | }
190 |
191 | /** @test */
192 | public function it_can_match_dynamic_parameters()
193 | {
194 | $this->withoutExceptionHandling();
195 | Pipe::fake();
196 |
197 | Pipe::match('trigger:name {text}', function ($text) {
198 | return "you said {$text}";
199 | });
200 |
201 | $this->pipe(['trigger' => 'name', 'text' => 'something']);
202 |
203 | Pipe::assertResponded(function ($response) {
204 | $response->assertOk()
205 | ->assertSee('you said something');
206 | });
207 | }
208 |
209 | /** @test */
210 | public function its_dynamic_pattern_match_checks_first_for_placeholder_name_in_request_then_for_default_cue_value()
211 | {
212 | Pipe::fake();
213 |
214 | Pipe::match('trigger:{text}', function ($text) {
215 | return "you said {$text}";
216 | });
217 |
218 | $this->pipe(['trigger' => 'something']);
219 |
220 | Pipe::assertResponded(function ($response) {
221 | $response->assertOk()
222 | ->assertSee('you said something');
223 | });
224 |
225 | Event::listen(IncomingPipeResponse::class, function () {
226 | $this->pipe(['trigger' => 'something', 'text' => 'another']);
227 |
228 | Pipe::assertResponded(function ($response) {
229 | $response->assertOk()
230 | ->assertSee('you said another');
231 | });
232 | });
233 | }
234 |
235 | /** @test */
236 | public function it_can_match_multiple_dynamic_parameters()
237 | {
238 | $this->withoutExceptionHandling();
239 | Pipe::fake();
240 |
241 | Pipe::any('{name} {other}', function ($name, $other) {
242 | return "$name $other";
243 | });
244 |
245 | $this->pipe(['name' => 'foo', 'other' => 'bar']);
246 |
247 | Pipe::assertResponded(function ($response) {
248 | $response->assertOk()
249 | ->assertSee('foo bar');
250 | });
251 | }
252 |
253 | /** @test */
254 | public function it_can_match_dynamic_parameters_to_any_request()
255 | {
256 | Pipe::fake();
257 |
258 | Pipe::any('name {text}', function ($text) {
259 | return "you said {$text}";
260 | });
261 |
262 | $this->pipe(['bla' => 'name', 'text' => 'something']);
263 |
264 | Pipe::assertResponded(function ($response) {
265 | $response->assertOk()
266 | ->assertSee('you said something');
267 | });
268 | }
269 |
270 | /** @test */
271 | public function it_doesnt_match_dynamic_parameters_if_the_request_contains_only_parts_of_the_cue()
272 | {
273 | Pipe::fake();
274 |
275 | Pipe::any('name {text}', function ($text) {
276 | return "you said {$text}";
277 | });
278 |
279 | $this->pipe(['bla' => 'nam', 'text' => 'something']);
280 |
281 | Pipe::assertResponded(function ($response) {
282 | $response->assertNotFound();
283 | });
284 | }
285 |
286 | /** @test */
287 | public function it_can_add_conditions_to_pipe_definitions()
288 | {
289 | Pipe::fake();
290 |
291 | Pipe::any('{name}', function ($name) {
292 | return $name;
293 | })->where('name', 'foo');
294 |
295 | $this->pipe(['name' => 'foo']);
296 |
297 | Pipe::assertResponded(function ($response) {
298 | $response->assertOk()
299 | ->assertSee('foo');
300 | });
301 | }
302 |
303 | /** @test */
304 | public function it_can_add_aliases_to_pipes()
305 | {
306 | Pipe::fake();
307 |
308 | Pipe::any('mshule', function () {
309 | return 'matched';
310 | })->alias(['mhule', 'mule']);
311 |
312 | $this->pipe(['foo' => 'mule']);
313 |
314 | Pipe::assertResponded(function ($response) {
315 | $response->assertOk()
316 | ->assertSee('matched');
317 | });
318 | }
319 |
320 | /** @test */
321 | public function it_can_identify_aliases_of_pipes_even_when_the_request_does_only_start_with_the_alias()
322 | {
323 | Pipe::fake();
324 |
325 | Pipe::any('mshule', function () {
326 | return 'matched';
327 | })->alias(['mhule', 'mule']);
328 |
329 | $this->pipe(['foo' => 'mule1']);
330 |
331 | Pipe::assertResponded(function ($response) {
332 | $response->assertOk()
333 | ->assertSee('matched');
334 | });
335 | }
336 |
337 | /** @test */
338 | public function it_can_add_aliases_predefined_to_pipes()
339 | {
340 | Pipe::fake();
341 |
342 | Pipe::alias(['mhule', 'mule'])->any('mshule', function () {
343 | return 'matched';
344 | });
345 |
346 | $this->pipe(['foo' => 'mule']);
347 |
348 | Pipe::assertResponded(function ($response) {
349 | $response->assertOk()
350 | ->assertSee('matched');
351 | });
352 | }
353 |
354 | /** @test */
355 | public function it_can_load_pipes_from_files()
356 | {
357 | Pipe::fake();
358 |
359 | Pipe::namespace('Test')->group(__DIR__.'/Fixtures/pipes.php');
360 |
361 | $this->pipe(['test' => 'ping']);
362 |
363 | Pipe::assertResponded(function ($response) {
364 | $response->assertOk()
365 | ->assertSee('pong');
366 | });
367 | }
368 |
369 | /** @test */
370 | public function it_ignores_global_helper_functions_as_cue()
371 | {
372 | Pipe::fake();
373 |
374 | Pipe::key('text')->group(function () {
375 | Pipe::match('report', function () {
376 | return 'it ignored report()';
377 | });
378 | });
379 |
380 | $this->pipe(['text' => 'report']);
381 |
382 | Pipe::assertResponded(function ($response) {
383 | $response->assertOk()
384 | ->assertSee('it ignored report()');
385 | });
386 | }
387 |
388 | /** @test */
389 | public function it_does_not_match_one_character_to_a_pipe_if_it_is_no_dynamic_param()
390 | {
391 | Pipe::fake();
392 |
393 | Pipe::key('text')->group(function () {
394 | Pipe::match('config', function () {
395 | return 'config pipe triggered';
396 | });
397 | });
398 |
399 | $this->pipe(['text' => 'c']);
400 |
401 | Pipe::assertResponded(function ($response) {
402 | $response->assertNotFound();
403 | });
404 | }
405 | }
406 |
--------------------------------------------------------------------------------
/tests/SubstituteBindingsTest.php:
--------------------------------------------------------------------------------
1 | loadMigrationsFrom(__DIR__.'/Fixtures/Migrations');
19 | }
20 |
21 | /** @test */
22 | public function it_can_bind_models_to_pipe_requests()
23 | {
24 | Pipe::fake();
25 |
26 | $todo = Todo::create(['name' => 'Foo Bar']);
27 |
28 | Pipe::middleware('pipe')->any('{todo}', function (Todo $todo) {
29 | return "You fetched {$todo->name} Todo";
30 | });
31 |
32 | $this->pipe(['todo' => $todo->id]);
33 |
34 | Pipe::assertResponded(function ($response) {
35 | $response->assertOk()
36 | ->assertSee('You fetched Foo Bar Todo');
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 |