Closed rogierborst closed 10 years ago
I think talking about converting snake_case to camelCase might be confusing the issue a little, but we can have this discussion with a slightly more common use-case.
Somebody asked me if Fractal would be good for converting their JSON into a format for their models to save. For me this was not something I ever considered doing for Fractal as the entire point for me was just to convert disparate and potentially complex data sources into consistent output for easy formatting and output.
Let's try out some theory code. It would be possible for me to have an input / output approach, where output is the current transform
method and input takes data in whatever format and converts it to an instance of whatever it is you are trying to make.
<?php namespace App\Transformer;
use League\Fractal\TransformerAbstract;
use Opp;
class OppTransformer extends TransformerAbstract
{
/**
* Available embeds
*
* @var array
*/
protected $availableEmbeds = [
'category',
'merchant',
];
/**
* Turn this item object into a generic array
*
* @param array $input
*
* @return Opp
*/
public function input(array $input)
{
$opp = new Opp([
'id' => (int) $input['id'],
'type' => (string) $input['type'],
'name' => (string) $input['name'],
'teaser' => (string) $input['teaser'],
'details' => (string) $input['details'],
'trending' => (bool) $input['trending'],
]);
$opp->ormMagicRelationship($input['category_id']);
$opp->ormMagicRelationship($input['merchant_id']);
}
/**
* Turn this item object into a generic array
*
* @return array
*/
public function output(Opp $opp)
{
return [
'id' => (int) $opp->id,
'type' => (string) $opp->type,
'name' => (string) $opp->name,
'teaser' => (string) $opp->teaser,
'details' => (string) $opp->details,
'trending' => (bool) $opp->trending,
];
}
/**
* Embed Category
*
* @param Opp $opp
*
* @return League\Fractal\Resource\Item
*/
public function embedCategory(Opp $opp)
{
return $this->item($opp->category, new CategoryTransformer);
}
/**
* Embed Merchant
*
* @param Opp $opp
*
* @return League\Fractal\Resource\Item
*/
public function embedMerchant(Opp $opp)
{
return $this->item($opp->merchant, new MerchantTransformer);
}
}
Is that the sort of thing you want? The embed logic would need to stay as output only, and you could make other instances and whatnot. You could hide more of the logic in your models/repositories/other domain code, and just use this as a general wrapper for things, but I have to wonder if this is even the responsibility of Fractal.
If Fractal handles output, nesting/embedding of relationships, etc, shouldn't your input be handled in a model/repository somewhere? There is no complicated output, nesting, etc for saving some shit. It's just passing data to some methods.
I like @philsturgeon's suggestion, reminds me of __sleep()
and __week()
@alexbilbie what about my last two paragraphs, where I say "yeah but fuck it"?
Does handling input result in creating (essentially) a data-transfer object that would then be consumed or tranformed into, say, a Laravel Model (for example)?
If so, I'm not sure I see the value.
Edit: Knowing that Opp()
is domain-level code for the app the above class might appear in, the Transformer now makes more sense. That's not really a data-xer object, but we're looking at the actual transformer of input to an business logic entity.
I think in any case, I still would be on the same page of "this isn't the responsibility of Fractal". Perhaps a separate package for handling input.
I think it would be most useful when doing DDD type code instead of model (such as Eloquent) driven. With ORMs, you can (generally) populate a model with the input, skipping the need for a transformer.
The biggest point I'd make is that it confuses the purpose of Fractal a bit. Definitely, in either case, make it a separate package.
Sorry I somehow ignored the last two paragraphs.
Fractal is an output library so it isn't it's responsibility, if you're expecting snake_case to be POSTed back to your API then that should be dictated in your API documentation
@alexbilbie That's not a very HATEOAS thing to do, depending on API documentation.
As for whether or not it's Fractals job, maybe not. But I can see myself creating the opposite of a Fractal transformer in my repository, writing yet another array with (to refer back to my original example) camelCase to snake_case transformations. I was just wondering if there wouldn't be a more efficient way to go about that.
@philsturgeon Btw, in your example code inside the input method, I guess you forgot to change $opp
into $input
.
I see you are repeating the same structure too, maybe that's just the only way to go about it. In that case, yeah, forget about it, it's probably not Fractals responsibility. In my current project (where I'm not using Fractal yet, but a personal transformer) I'm about to start working with input, so I haven't yet given it much thought. Will check back if I can think of something clever :)
@roggel you read the initial email, not the actual issue example which was updated earlier. :)
@roggel output needs a transformer (lets be honest, its a callback to convert some random data to an array) but it also needs:
Fractal supports (or will support) all of the above features.
Input requires:
After that, you output the result, using Fractal. Your input method could be ANYWHERE else, and has no relation to anything else that fractal currently does or will do.
Okay, that's a really clear explanation. There is absolutely no need for Fractal to support this.
Now, when I get to saving input in my current project, it's good to know I don't have to come up with something clever. Thanks!
@roggel regarding HATEOAS I didn't consider it in my response because I largely disagree with it's principles, don't be a dick and make me work out how your API works, just document it
One thing doesn't have to exclude another. I'd prefer to do both, provide an api that documents itself and provide separate, extensive documentation.
@philsturgeon I think "Input requires: Calling some methods to save stuff" Might be a bit of an oversimplification. You could come up with a large list of needs for Input which might be abstracted from "just save some values." Validation and creation of nested/related data etc. But I do think it's outside of fractal's scope, especially since these other things are often handled at different layers.
@awright022 I stand by my comments. Validation is sometimes just if ($foo > 3)
but has a million packages for it already. So use those for that, or ifs, then carry on with "save stuff". It's still not something fractal needs to care about.
As for nested/related data, that is not a concern for input. You're more likely to post a "category_id" than post an entire { category : { id : 5, name : "Cheese", icon : "cheese.gif" } }
so the complications of nested data remain purely as an exercise for the output.
Relevant update on https://github.com/thephpleague/fractal/issues/65#issuecomment-388097149
I was wondering, isn't this a very one-way transformation? Let's say my repo is using snake_case and with fractal I'm outputting as camelCase.
Now the client wants to create a new instance and for all he knows, we're using camelCase throughout our api. So he'll post with camelCase definitions.
Does fractal then provide a way to transform back to snake_case?