Can't seem to register relationship in trait


#1

Hello @virk @romain.lanz . I’m trying to register a relationship in a trait, like this:

    Model.prototype.subscriptions = function () {
      return this.hasMany(require('../Subscription'))
    }

but i end up with an error:

Cannot read property 'iterator' of undefined

Please any idea why ?

Thanks a lot for your support.


#2

Why do you need to do it this way?

According to the code, hasMany allows for name or model instance.

So, you probably want to use use in this case to get the model instance.

return this.hasMany(use('App/Models/Subscription'))

or just

return this.hasMany('App/Models/Subscription')

#3

hello @moltar . thanks for your reply.

The reason I need to do it this way is because I am developing an external package, and this model won’t be in the App/Models/ directory. I checked out the source for the hasMany function, and here it is:


  hasMany (relatedModel, primaryKey, foreignKey) {
    relatedModel = typeof (relatedModel) === 'string' ioc.use(relatedModel) : relatedModel

    primaryKey = primaryKey || this.constructor.primaryKey
    foreignKey = foreignKey || this.constructor.foreignKey

    return new HasMany(this, relatedModel, primaryKey, foreignKey)
  }

Looks like it accepts the model directly, and that’s what I was trying to do, but everything blew up with the iterator error. Please kindly have another look, thanks .


#4

Are you working on a Stripe module by any chance? :wink:

You can study how other modules do it, for example adonis-acl.

Notice that in a provider it registers the models in the IoC and then those registered aliases can be used as rels.

E.g.:


#5

@bahdcoder can you please share what’s in the Subscription model?


#6

@virk here’s the model


'use strict'

const Model = use('Model')

/**
 * The subscription model
 */
class Subscription extends Model {
  /**
   * The database fields to be treated as dates
   *
   * @returns {Array}
   */
  static get dates () {
    return super.dates.concat([
      'trial_ends_at', 'ends_at'
    ])
  }

  /**
   * Indicates if the plan change should be prorated.
   *
   * @returns {Boolean}
   */
  static get prorate () {
    return true
  }

  /**
   * Get the user that owns the subscription.
   * @returns {Object}
   */
  user () {
    return this.owner()
  }

  /**
   * A subscription belongs to related model
   * @returns {Object} BelongsTo
   */
  owner () {
    return this.belongsTo('App/Models/User')
  }

  /**
   * Determine if the subscription is active, on trial,
   * or within its grace period.
   *
   * @method valid
   *
   * @returns {Boolean}
   */
  valid () {
    return this.active() || this.onTrial() || this.onGracePeriod()
  }

  /**
   * Determine if the subscription is active.
   *
   * @method active
   *
   * @returns {Boolean}
   */
  active () {
    return this.ends_at === null || this.onGracePeriod()
  }

  /**
   * Determine if the subscription is no longer active.
   *
   * @method cancelled
   *
   * @returns {Boolean}
   */
  cancelled () {
    return this.ends_at !== null
  }

  /**
   * Determine if the subscription is within its trial period.
   *
   * @method onTrial
   *
   * @returns {Boolean}
   */
  onTrial () {}

  /**
   * Determine if the subscription is within its grace period
   * after cancellation.
   *
   * @method onGracePeriod
   *
   * @returns {Boolean}
   */
  onGracePeriod () {}

  /**
   * Increment the quantity of the subscription.
   *
   * @method incrementQuantity
   *
   * @param {Number} count
   *
   * @returns {Boolean}
   */
  async incrementQuantity (count = 1) {
    await this.updateQuantity(this.quantity + count)

    return this
  }

  /**
   * Increment the quantity of the subscription, , and invoice immediately.
   *
   * @method incrementAndInvoice
   *
   * @param {Number} count
   *
   * @returns {Boolean}
   */
  async incrementAndInvoice (count = 1) {
    await this.incrementQuantity(count)

    await this.user.invoice()

    return this
  }

  /**
   * Decrement the quantity of the subscription.
   *
   * @method decrementQuantity
   *
   * @param {Number} count
   *
   * @returns {Boolean}
   */
  async decrementQuantity () {}

  /**
   * Update the quantity of the subscription.
   *
   * @method decrementQuantity
   *
   * @param {Number} count
   *
   * @returns {Boolean}
   */
  async updateQuantity () {}

  /**
   * Force the trial to end immediately.
   *
   * @method skipTrial
   *
   * @param {Number} count
   *
   * @returns {Boolean}
   */
  async skipTrial () {}
}

module.exports = Subscription



#7

@moltar i checked out the acl package you shared, and looks like enniel (package creator) was booting the model if it wasn’t booted. I did the same and everything works good now. I’m still to dive deep to understand exactly how things are wired up, but for now i can proceed. thaaanks a lot.

  /**
   * Bind the Subscription model
   * @returns {void}
   */
  _bindSubscriptionModel () {
    this.app.bind('Cashier/Subscription', () => {
      const Subscription = require('../models/Subscription')

      Subscription._bootIfNotBooted()

      return Subscription
    })
  }

thanks @virk for taking a look .


#8

FYI @bahdcoder - just announced: https://stripe.com/billing

Might as well just do the integration for that, as I think it supersedes the previous subscription stuff.


#9

thanks a lot @moltar. It was very nice of you to point this out. I had already made a lot of progress on the stripe billing package. I’m having a look at Stripe Billing now to see what’s changed.