Override hasId() so POST is used with custom primaryKey
Closed this issue · 3 comments
Thanks for the sweet library. I have a model that has a typical auto incrementing integer key; however, it has a 'friendly' token that we use as identifiers in the routes. This friendly token is also used as a sort of idempotency token (basically a reference to an external id):
Route::get('experiment_trials/{experimentTrial:token}', 'Api\Commerce\ExperimentTrialController@show');
Route::post('experiment_trials', 'Api\Commerce\ExperimentTrialController@store');
In this world I want to be able to hit my /show endpoint with the friendly token. But when I save it, I'm running into issues.
Here is a Model with a custom primaryKey
import Model from './Model';
export default class ExperimentTrial extends Model {
resource() {
return 'experiment_trials';
}
// Define the primary key of the model
primaryKey() {
return 'token';
}
static async findOrCreate(identifier, attributes) {
try {
// We await here because we need to know if we 404, which means resolving the promise
return await ExperimentTrial.$find(identifier);
} catch (error) {
// We end up here if the find fails
const trial = new ExperimentTrial(attributes);
return trial.save();
}
}
}
Works great for the find, but save() uses hasId() to decide on PUT or POST, which in this case always throws my findOrCreate into the PUT option. This fails, because I do not have a model with that token, I want to create it.
Do you have any suggestions for how to explicitly force ._create() or override the hasId() function? Both seem to be private at the moment.
Hi @msencenb! As your approach is not currently supported by the system, I think you can workaround this issue by using the config
method.
The example below will fix the url and force a POST
request.
trial.config({ method: 'POST', url: `${this.baseURL()}/${this.resource()}` }).save()
Note that it still using the internal _update
method. We are just overriding the request config.
Let me know if it worked! :)
Awesome, thank you @JoaoPedroAS51. Using config as you suggested did the trick! Here's my final working code in case it's helpful to someone in the future:
import Model from './Model';
export default class ExperimentTrial extends Model {
resource() {
return 'experiment_trials';
}
// Define the primary key of the model
primaryKey() {
return 'token';
}
static async findOrCreate(identifier, attributes) {
try {
// We await here because we need to know if we 404, which means resolving the promise
return await ExperimentTrial.$find(identifier);
} catch (error) {
// Override the _update method using config, so we POST instead of PUTing (token is used for lookup, but should not determine PUT vs POST in this save case)
const trial = new this(attributes);
return trial.config({ method: 'POST', url: `${trial.baseURL()}/${trial.resource()}` }).save();
}
}
}
I'm happy to know it worked! 😃