Many to Many Polymorphic Relations from Laravel to Angular and back

Posted: 2014-07-05 01:36:17

This will cover having a polymorphic relationship in Laravel, showing it on a form in Angular and saving it back to Laravel. Overall it is well documented here at the great docs at Laravel but this post might help in a few areas.

As someone defines Polymorphic Relationships there

"Polymorphic relations allow a model to belong to more than one other model, on a single association. For example, you might have a photo model that belongs to either a staff model or an order model. "

Output the data to Angular

Taking the User Model as an example I query the user but then before returning the data to Angular I append the polymorphic results. In this case we will call it Specialties

protected function transformOutgoingData($data)
    {
        $data->specialties = $data->specialties()->getResults()->lists('id');
        return $data;
    }

This returns just an array to Angular to deal with. When sending it an Object trouble getting Angular to set the selected options. The hard part, since by default it just worked, was to send not only the 2 selected Specialties but the 20 other possible ones and make the list out of that.

#angular html file


<

div class="form-group" ng-if="userHasAdminRole(user)"  markdown="1">
        <label>Specialties:</label>
        <select ng-options="specialty.id as specialty.name for specialty in user.specialties_all"
                name="specialty"
                multiple
                ng-model="user.specialties"
                ng-required="true"
                class="form-control">
        </select>
    </div>

The specialties_all comes from the method I run on the data before output it which I do anyways on many data models. This comes right before the transformOutgoingData.

    protected function addDataToOutGoingModel($user)
    {
        $user = $this->addAllSpecialtiesToModel($user);
        return $user;
    }

This way all my select lists on that page and other data lists, checkboxes etc have the data they need.

Finally when the person clicks Submit it is sent back to Laravel. So far I could not just save or update the user with this info. For example

#app/models/User.php
    public function update($user_id, $all)
    {
        $all = $this->transformIncomingData($all);
        try {
            $user = \User::findOrFail($user_id);
        }
        catch(\Expection $e){
            throw new \Exception("User not found");
        }

        $user->update($all);
        $user = $this->saveDataToModel($user);
        return $user;
    }

The $user->update($all); will crash with this extra "specialties" data coming in with this error.

"preg_replace(): Parameter mismatch, pattern is a string while replacement is an array"

So instead I do a step before and after

Before I run transformIncomingData and unset the $all['specialties'] key I get from Input::all() and save that to a property

    protected function transformIncomingData($data)
    {
        $data = ( isset($data['specialties'])) ? $this->transformSpecialtiesIncoming($data) : $data;
        return $data;
    }

which goes to

    protected function transformSpecialtiesIncoming($data)
    {
        $this->original_specialities = $data['specialties'];
        unset($data['specialties']);
        return $data;
    }

Then after I update the User I run saveDataToModel($user) which triggers this method

    protected function setSpecialties($user)
    {
        $user->specialties()->sync($this->original_specialities);
        return $user;
    }

Finally back to Angular with the results.

Another good way to tie all this together is the Model Observer Pattern noted here http://laravel.com/docs/eloquent#model-observers