├── .github └── workflows │ └── php.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── phpunit.xml ├── src └── Database │ └── Eloquent │ └── Concerns │ └── HasCustomMorphMap.php └── tests ├── HasCustomMorphMapTest.php └── Models ├── Comment.php ├── NewTag.php ├── Post.php └── Tag.php /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP Composer 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Validate composer.json and composer.lock 21 | run: composer validate --strict 22 | 23 | - name: Cache Composer packages 24 | id: composer-cache 25 | uses: actions/cache@v3 26 | with: 27 | path: vendor 28 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 29 | restore-keys: | 30 | ${{ runner.os }}-php- 31 | 32 | - name: Install dependencies 33 | run: composer install --prefer-dist --no-progress 34 | 35 | - name: Run test suite 36 | run: ./vendor/bin/phpunit 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Laravel Custom Morph Map Changelog 2 | 3 | ### 1.0.4 4 | - Fix README after ownership change. 5 | 6 | ### 1.0.3 7 | Change ownership from **mcucen** to **moneo** 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) moneo 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://banners.beyondco.de/Custom%20Morph%20Map.png?theme=light&packageManager=composer+require&packageName=moneo%2Flaravel-morphmap&pattern=anchorsAway&style=style_1&description=Custom+morphMap+for+each+relation&md=1&showWatermark=0&fontSize=100px&images=link) 2 | 3 | # Laravel Custom Morph Map 4 | 5 | This package provides use different `morphMap` for different relationships. 6 | 7 | If your polymorphic relation structures does not satisfy with each other, this package lets you use custom map for each 8 | relation definition. 9 | 10 | PS: By default, Laravel does not support custom mapping for each relation. 11 | 12 | ## Installation 13 | 14 | composer require moneo/laravel-morphmap 15 | 16 | ## Usage 17 | ```php 18 | class Post extends Model 19 | { 20 | // 🚀 Add HasCustomMorphMap trait! 21 | use HasCustomMorphMap; 22 | 23 | public function __construct(array $attributes = []) 24 | { 25 | // 👋 Custom definition for Category relation! 26 | $this->customMorphMap = [ 27 | Category::class => 'post', 28 | ]; 29 | 30 | // 👋 Default for all others! (__CLASS__ definition is default. You don't need to add this.) 31 | $this->defaultMorphType = Post::class; 32 | 33 | parent::__construct($attributes); 34 | } 35 | 36 | public function tags(): MorphToMany 37 | { 38 | return $this->morphToMany(Tag::class, 'taggable'); 39 | } 40 | 41 | public function categories(): MorphToMany 42 | { 43 | return $this->morphToMany(Category::class, 'categoryable'); 44 | } 45 | } 46 | ``` 47 | 48 | This usage example covers the example data below. 49 | 50 | ### Example Case: 51 | 52 | Assume you use these tables in your project. 53 | 54 | ``` 55 | posts 56 | id - integer 57 | name - string 58 | 59 | videos 60 | id - integer 61 | name - string 62 | 63 | tags 64 | id - integer 65 | name - string 66 | 67 | categories 68 | id - integer 69 | name - string 70 | 71 | taggables 72 | tag_id - integer 73 | taggable_id - integer 74 | taggable_type - string 75 | 76 | categoryables 77 | category_id - integer 78 | categoryable_id - integer 79 | categoryable_type - string 80 | ``` 81 | 82 | #### Example Data: 83 | 84 | #### posts 85 | 86 | | id | name | 87 | |----|-------------------------------| 88 | | 1 | Easy Seralization in Doctrine | 89 | 90 | #### videos 91 | 92 | | id | name | 93 | |----|----------------------------------------| 94 | | 1 | Beyond Controllers in Laravel Projects | 95 | 96 | #### tags 97 | 98 | | id | name | 99 | |----|----------| 100 | | 1 | PHP | 101 | | 2 | Laravel | 102 | | 3 | Doctrine | 103 | 104 | #### categories 105 | 106 | | id | name | 107 | |----|-----------------| 108 | | 1 | Articles | 109 | | 2 | Video Tutorials | 110 | 111 | #### taggables 112 | 113 | | tag_id | taggable_id | taggable_type | 114 | |--------|-------------|------------------| 115 | | 1 | 1 | App\Models\Post | 116 | | 3 | 1 | App\Models\Post | 117 | | 1 | 1 | App\Models\Video | 118 | | 2 | 1 | App\Models\Video | 119 | 120 | #### categoryables 121 | 122 | | category_id | categoryable_id | categoryable_type | 123 | |-------------|-----------------|-------------------| 124 | | 1 | 1 | post | 125 | | 3 | 1 | post | 126 | | 1 | 1 | video | 127 | | 2 | 1 | video | 128 | 129 | ## Contributing 130 | Contributions are always welcome, [thanks to all of our contributors](https://github.com/moneo/laravel-morphmap/graphs/contributors)! 131 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "moneo/laravel-morphmap", 3 | "description": "Custom morphMap support for Laravel Framework", 4 | "type": "library", 5 | "version": "1.0.4", 6 | "autoload": { 7 | "psr-4": { 8 | "Moneo\\LaravelMorphMap\\": "src/" 9 | } 10 | }, 11 | "autoload-dev": { 12 | "psr-4": { 13 | "Moneo\\LaravelMorphMap\\Tests\\": "tests/" 14 | } 15 | }, 16 | "authors": [ 17 | { 18 | "name": "Mücahit Cücen", 19 | "email": "mucahitcucen@gmail.com" 20 | } 21 | ], 22 | "license": "MIT", 23 | "require": { 24 | "php": ">=8.0.0", 25 | "illuminate/database": "^8.12|^9.0|^10.0|^11.0" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^9.0", 29 | "orchestra/testbench": "^6.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ./tests 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Database/Eloquent/Concerns/HasCustomMorphMap.php: -------------------------------------------------------------------------------- 1 | customMorphMap($related) => __CLASS__, 19 | ]); 20 | 21 | $args = func_get_args(); 22 | 23 | return parent::morphToMany(...$args); 24 | } 25 | 26 | private function customMorphMap(string $related): string 27 | { 28 | if (array_key_exists($related, $this->customMorphMap)) { 29 | return $this->customMorphMap[$related]; 30 | } 31 | 32 | return $this->defaultMorphType; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/HasCustomMorphMapTest.php: -------------------------------------------------------------------------------- 1 | set('database.default', 'testbench'); 15 | $app['config']->set('database.connections.testbench', [ 16 | 'driver' => 'sqlite', 17 | 'database' => ':memory:', 18 | 'prefix' => '', 19 | ]); 20 | } 21 | 22 | /** @test */ 23 | public function it_registers_custom_morph_map() 24 | { 25 | 26 | Relation::morphMap( 27 | [ 28 | 'tag' => 'Moneo\LaravelMorphMap\Tests\Models\Tag', 29 | ] 30 | ); 31 | 32 | $post = new Post(); 33 | $post->morphToMany('Moneo\LaravelMorphMap\Tests\Models\Tag', 'taggable'); 34 | 35 | $morphMap = Relation::morphMap(); 36 | $this->assertArrayHasKey('tag', $morphMap); 37 | $this->assertEquals('Moneo\LaravelMorphMap\Tests\Models\Tag', $morphMap['tag']); 38 | } 39 | 40 | /** @test */ 41 | public function it_registers_custom_morph_map_across_multiple_models() 42 | { 43 | Relation::morphMap( 44 | [ 45 | 'tag' => 'Moneo\LaravelMorphMap\Tests\Models\Tag', 46 | 'comment' => 'Moneo\LaravelMorphMap\Tests\Models\Comment', 47 | ] 48 | ); 49 | 50 | $post = new Post(); 51 | $post->morphToMany('Moneo\LaravelMorphMap\Tests\Models\Tag', 'taggable'); 52 | 53 | $comment = new Comment(); 54 | $comment->morphToMany('Moneo\LaravelMorphMap\Tests\Models\Comment', 'commentable'); 55 | 56 | $morphMap = Relation::morphMap(); 57 | 58 | $this->assertEquals('Moneo\LaravelMorphMap\Tests\Models\Tag', $morphMap['tag']); 59 | $this->assertEquals('Moneo\LaravelMorphMap\Tests\Models\Comment', $morphMap['comment']); 60 | } 61 | 62 | /** @test */ 63 | public function it_allows_dynamic_changes_to_the_custom_morph_map() 64 | { 65 | Relation::morphMap([ 'tag' => 'Moneo\LaravelMorphMap\Tests\Models\Tag' ]); 66 | 67 | $post = new Post(); 68 | $post->morphToMany('Moneo\LaravelMorphMap\Tests\Models\Tag', 'taggable'); 69 | 70 | $this->assertEquals('Moneo\LaravelMorphMap\Tests\Models\Tag', Relation::morphMap()['tag']); 71 | 72 | Relation::morphMap([ 'tag' => 'Moneo\LaravelMorphMap\Tests\Models\NewTag' ]); 73 | 74 | $this->assertEquals('Moneo\LaravelMorphMap\Tests\Models\NewTag', Relation::morphMap()['tag']); 75 | } 76 | 77 | /** @test */ 78 | public function it_handles_invalid_custom_morph_map_values_gracefully() 79 | { 80 | Relation::morphMap([ 'tag' => null ]); 81 | 82 | $post = new Post(); 83 | 84 | $this->assertNull(Relation::morphMap()['tag']); 85 | } 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /tests/Models/Comment.php: -------------------------------------------------------------------------------- 1 | customMorphMap = [ 21 | 'commentable' => self::class, 22 | ]; 23 | } 24 | 25 | public function commentable(): MorphTo 26 | { 27 | return $this->morphTo(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Models/NewTag.php: -------------------------------------------------------------------------------- 1 | customMorphMap = [ 21 | 'tag' => 'Moneo\LaravelMorphMap\Tests\Models\Tag', 22 | ]; 23 | } 24 | 25 | public function tags(): MorphToMany 26 | { 27 | return $this->morphToMany('Moneo\LaravelMorphMap\Tests\Models\Tag', 'taggable'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Models/Tag.php: -------------------------------------------------------------------------------- 1 |