fractal icon indicating copy to clipboard operation
fractal copied to clipboard

[Question] CamelCasing included relationships

Open renspoesse opened this issue 8 years ago • 9 comments

I'm trying to include a relationship and CamelCase its key in the output. Is there a way to do this in Fractal without altering the function name in the model?

Model:

class Project extends Model
{
    public function target_locations()
    {
        return $this->belongsToMany('Location');
    }
}

Transformer:

class ProjectTransformer extends TransformerAbstract
{    
    protected $defaultIncludes = ['target_locations'];

    public function transform(Project $obj)
    {
        return [

            'id' => (int)$obj->id
        ];
    }

    public function includeTargetLocations(Project $obj)
    {
        return $obj->target_locations ? $this->collection($obj->target_locations, new LocationTransformer()) : null;
    }
}

The JSON output that I'd like:

{
    id: 1,
    targetLocations: []
}

Thanks in advance for any answer!

renspoesse avatar Nov 21 '16 14:11 renspoesse

This is a bit tricky if you don't want to change $defaultIncludes.

You could do a quick extending serializer like this:

class CamelCaseArraySerializer extends ArraySerializer
{
    /**
     * Serialize a collection.
     *
     * @param string $resourceKey
     * @param array  $data
     *
     * @return array
     */
    public function collection($resourceKey, array $data)
    {
        return parent::collection($resourceKey, $this->convertKeysToCamelCase($data));
    }

    /**
     * Serialize an item.
     *
     * @param string $resourceKey
     * @param array  $data
     *
     * @return array
     */
    public function item($resourceKey, array $data)
    {
        return parent::item($resourceKey, $this->convertKeysToCamelCase($data));
    }

    /**
     * Convert keys of an array from snake to camel case.
     *
     * @param $data
     * @return array
     */
    private function convertKeysToCamelCase($data) {
        $keys = array_map(function ($i) {
            $parts = explode('_', $i);
            return array_shift($parts). implode('', array_map('ucfirst', $parts));
        }, array_keys($data));

        return array_combine($keys, $data);
    }
}

Then pass it to the manager like this: $manager = (new Manager())->setSerializer(new CamelCaseArraySerializer());

But this will camel case all of the keys, and you may just want to camel case included resource keys? This may be hard to do without giving the serializer a list of keys to only look for, specific to your application. But there may be an option I'm not thinking of that someone else can chime in with.

bradbforbes avatar Nov 27 '16 17:11 bradbforbes

Hey Brad! I've adapted your serializer but it doesn't seem to work for included items. Though even if it did, wouldn't this fix the problem at the "wrong" level? I.e., at serialization level instead of transformation level.

renspoesse avatar Nov 28 '16 12:11 renspoesse

@renspoesse you should be able to change the key in $default_includes to camel case and have it work. Have you tried that?

greydnls avatar Nov 28 '16 13:11 greydnls

Sorry about that -- it looks like I forgot a filterIncludes() override in my serializer, but it would be implemented in the same fashion as the other methods if you decided to use it.

I agree with you about this being more appropriate in the transformation domain, but I'm not sure how it could be done easily. If you take a look at TransformerAbstract::includeResourceIfAvailable(), you'll see that it's really the $default_includes/$available_includes strings that dictate the key names you want to camel case. You could extend TransformerAbstract and override includeResourceIfAvailable() in a transformer that your model transformers then implement, but then you're overriding a private method implied to be "final." Though I may be missing another option that someone else can point out.

bradbforbes avatar Nov 29 '16 00:11 bradbforbes

@bradbforbes thanks, we've extended TransformerAbstract to fix it (for now)!

You're right though, we had to override a private method so I still think this is a bit of an issue in flexibility of the transformer.

renspoesse avatar Nov 29 '16 16:11 renspoesse

Any solution to this yet?

KieronWiltshire avatar Apr 28 '21 13:04 KieronWiltshire

same problem

image

i want convert to underscore

minhtamwebmaster avatar May 21 '21 05:05 minhtamwebmaster

This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 4 weeks if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Apr 16 '22 06:04 stale[bot]

I have solved this issue, by force include (available or default) with underscored attributes. Because laravel has automate convert camelCase into snake-case in all attributes (getter) For example, image

illusi03 avatar Aug 13 '22 15:08 illusi03