robsontenorio/vue-api-query

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! 😃