Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relation::morphMap incorrectly merges maps with integer keys #21457

Closed
v0ctor opened this issue Sep 29, 2017 · 1 comment
Closed

Relation::morphMap incorrectly merges maps with integer keys #21457

v0ctor opened this issue Sep 29, 2017 · 1 comment

Comments

@v0ctor
Copy link
Contributor

v0ctor commented Sep 29, 2017

  • Laravel Version: 5.5.13
  • PHP Version: 7.0.24
  • Database Driver & Version: MySQL 5.6.37

Description:

When using integer custom polymorphic types, Relation::morphMap does not merge them properly because it uses array_merge(), which treats keys differently depending on their type:

If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended. PHP Documentation.

This difference in behavior may be a problem in some cases. I'll give an example to reproduce the problem.

Before continuing I would like to clarify that I am aware that it would be more appropriate to use string keys and the enum type, but I can not make changes to the project model, 🤷‍♂️.

Steps To Reproduce:

Imagine you have the following morph map:

// Note that there's no key 0 or 3
$morphMap = [
    1 => Post::class,
    2 => Image::class,
    4 => Video::class,
];

Now, if you call to Relation::morphMap($morphMap), the value of Relation::$morphMap would be the following:

array(3){
    1 => Post::class,
    2 => Image::class,
    4 => Video::class,
}

But if you try to set the morph map again with new values, as it makes no sense removing the old ones before merging them, array_merge will merge them like this:

Relation::morphMap([
    1 => Post::class,
    2 => Image::class,
    4 => Video::class,
    5 => Poll::class,
]);
array(7){
    0 => Post::class,
    1 => Image::class,
    2 => Video::class,
    3 => Post::class,
    4 => Image::class,
    5 => Video::class,
    6 => Poll::class,
}

I think the expected behavior should be the following:

array(4){
    1 => Post::class,
    2 => Image::class,
    4 => Video::class,
    5 => Poll::class,
}

Proposed solution

Since a morph map is an associative array rather than a sequential one, I propose to modify the implementation of Relation::morphMap as follows:

public static function morphMap(array $map = null, $merge = true)
{
    $map = static::buildMorphMapFromModels($map);

    if (is_array($map)) {
        static::$morphMap = $merge && static::$morphMap
            ? $map + static::$morphMap : $map;
    }

    return static::$morphMap;
}

By using the array "+" operator there can not be duplicate values. Note that the $map parameter is the left-hand value of the operation so that it is possible to replace values by calling again to Relation::morphMap.

@v0ctor v0ctor closed this as completed Sep 29, 2017
edmandiesamonte added a commit to edmandiesamonte/framework that referenced this issue Oct 2, 2017
* upstream/5.5: (84 commits)
  Correct docBlock depenency on syncWithoutDetaching (laravel#21478)
  update v5.5 changelog
  allow to specify the queue while scheduling of queued jobs (laravel#21473)
  [5.5] Clear CountQuery "select" bindings since we're overriding the columns anyway (laravel#21468)
  add interface
  access pivot on resource
  update v5.5 changelog
  extract trait
  Fix docblock in Route (laravel#21454)
  fix test
  formatting
  Fix Relation::morphMap() merge (issue laravel#21457). (laravel#21458)
  extract AnonymousResourceCollection into class to allow serialization
  revert relationship limits
  Fix spelling of 'optionally'
  [5.5] Fix Collection::contains() when the found value is null (laravel#21442)
  Allow passing a callback to "with" (laravel#21445)
  [5.5] Add relation and model attributes in RelationNotFoundException (laravel#21426)
  [5.5] Make sure sql for virtual columns is added after the unsigned modifier (laravel#21441)
  vendor:publish options alphabetized (laravel#21412)
  ...
@hakuno
Copy link

hakuno commented Jan 29, 2018

@victordzmr Great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants