Trailblazor operation -> collection :attribute(has_many through association) with populator -> How to populate the Reform::Form form array of slugs?

huangapple go评论96阅读模式
英文:

Trailblazor operation -> collection :attribute(has_many through association) with populator -> How to populate the Reform::Form form array of slugs?

问题

以下是您要翻译的内容:

"I'm using gem trailblazer.

I have following structure.

Following two models

class Company
  has_many :acquirable_source_companies,
           class_name: 'AcquirableSourceCompany',
           foreign_key: 'acquirer_company_id',
           inverse_of: :acquirer_company

  has_many :acquires_from_companies, through: :acquirable_source_companies, source: :acquires_from_company
end


class AcquirableSourceCompany
  belongs_to :acquirer_company,
             class_name: 'Company',
             foreign_key: 'acquirer_company_id',
             inverse_of: :acquires_from_companies

  belongs_to :acquires_from_company, class_name: 'Company', foreign_key: 'source_company_id'
end

Now I just want to use multi select to select acquirable companies when I edit parent companies.

In the frontend I'm having following

f.input :acquires_from_companies,
                as: :select,
                collection: ::Company.where.not(type: :parent).pluck(:slug),
                include_blank: true,
                selected: @company.acquires_from_companies.pluck(:slug),
                input_html: { multiple: true, class: 'select2' }

Now when I save this form, it passes the array of string in following format.

Parameters: {"authenticity_token"=>"<token>", "company"=>{"acquires_from_companies"=>["", "child_1", "child_2", "child_3"], ...}, id: 1 }

the operation class looks like following

module Companies
  module Operations
    class Update &lt; ParentClass
      include Model
      model ::Company

      contract do
        collection :acquires_from_companies
      end
    end
  end
end

And the collection have array of string.

What is best way to convert it to Reform::Form or ActiveRecord collection.

before the parameter get validated.

I've tried following

contract do
  collection :acquires_from_companies, populator: :companies

  def companies(fragment:, **)
    acquires_from_companies = acquires_from_companies.push(Company.find_by(slug: fragment))
  end
end

But that empty string is not working out.

Also if I somehow manage to get rid of empty string from array.
Should I build company object one by one or all together.

for any option either one by one or all together what should be the ideal implementation?

Also please let me know if more details are required to understand the question."

英文:

I'm using gem trailblazer.

I have following structure.

Following two models

class Company
  has_many :acquirable_source_companies,
           class_name: &#39;AcquirableSourceCompany&#39;,
           foreign_key: &#39;acquirer_company_id&#39;,
           inverse_of: :acquirer_company

  has_many :acquires_from_companies, through: :acquirable_source_companies, source: :acquires_from_company
end


class AcquirableSourceCompany
  belongs_to :acquirer_company,
             class_name: &#39;Company&#39;,
             foreign_key: &#39;acquirer_company_id&#39;,
             inverse_of: :acquires_from_companies

  belongs_to :acquires_from_company, class_name: &#39;Company&#39;, foreign_key: &#39;source_company_id&#39;
end

Now I just want to use multi select to select acquirable companies when I edit parent companies.

In the frontend I'm having following

f.input :acquires_from_companies,
                as: :select,
                collection: ::Company.where.not(type: :parent).pluck(:slug),
                include_blank: true,
                selected: @company.acquires_from_companies.pluck(:slug),
                input_html: { multiple: true, class: &#39;select2&#39; }

Now when I save this form, it passes the array of string in following format.

Parameters: {&quot;authenticity_token&quot;=&gt;&quot;&lt;token&gt;&quot;, &quot;company&quot;=&gt;{&quot;acquires_from_companies&quot;=&gt;[&quot;&quot;, &quot;child_1&quot;, &quot;child_2&quot;, &quot;child_3&quot;], ...}, id: 1 }

the operation class looks like following

module Companies
  module Operations
    class Update &lt; ParentClass
      include Model
      model ::Company

      contract do
        collection :acquires_from_companies
      end
    end
  end
end

And the collection have array of string.

What is best way to convert it to Reform::Form or ActiveRecord collection.

before the parameter get validated.

I've tried following

contract do
  collection :acquires_from_companies, populator: :companies

  def companies(fragment:, **)
    acquires_from_companies = acquires_from_companies.push(Company.find_by(slug: fragment))
  end
end

But that empty string is not working out.

Also if I somehow manage to get rid of empty string from array.
Should I build company object one by one or all together.

for any option either one by one or all together what should be the ideal implementation?

Also please let me know if more details are required to understand the question.

答案1

得分: 2

首先,正如你提到的,acquires_from_companies 数组位于 options 参数中,所以你可以使用整个参数 options,而不是参数中的 fragment 键。

在选项中会有一个 :doc 键,其中包含了所有的参数。

类似下面这样的方式,使用两个选项:

  1. 更改模型
contract do
  collection :acquires_from_companies, populator: :companies

  def companies(options)
    model.acquires_from_companies = Company.where(slug: options[:doc]['acquires_from_companies'])
  end
end
options[:doc]['acquires_from_companies'] 
  1. 更改合同
contract do
  collection :acquires_from_companies, populator: :companies

  def companies(options)
    self.acquires_from_companies = Company.where(slug: options[:doc]['acquires_from_companies'])
  end
end

上述两个示例中的 modelself 的区别是...

当你选择 model.attribute

你直接更改了这5个步骤,因此验证可能会因为参数中没有正确的值而失败。因为你已经更新的是模型属性而不是参数。

当你选择 self.attribute

你在第2步中进行更改。
你跟随流程,所有后续步骤都会在参数中具有更新后的值。

我建议更改合同/参数,而不是模型。

但只是为了分享,我也分享了使用模型的选项。

无论如何,在填充器中应避免对模型进行更新。
但如果你只是不需要验证并且不关心中间步骤,你可以选择模型更新选项。

Trailblazer 是一个很棒的 gem,使用它后,你可能不会再喜欢普通的结构了。

英文:

First as you mentioned the array of slugs is in acquires_from_companies so you can do is use the whole argument options instead of on key fragment in the argument.

There will be a :doc present in the options, and that have all the parameters.

Something like the following with two options.

  1. change the model
contract do
  collection :acquires_from_companies, populator: :companies

  def companies(options)
    model.acquires_from_companies = Company.where(slug: options[:doc][&#39;acquires_from_companies&#39;])
  end
end
options[:doc][&#39;acquires_from_companies&#39;] 
  1. change the contract
contract do
  collection :acquires_from_companies, populator: :companies

  def companies(options)
    self.acquires_from_companies = Company.where(slug: options[:doc][&#39;acquires_from_companies&#39;])
  end
end

These could be normal flow of steps in a trailblazer operation.

  1. Get the model object
  2. Build nested form tree
  3. Update form tree from params
  4. Run validation
  5. Assign the attributes to the model

The difference between model and self in the above two examples is…

When you choose model.attribute

You directly change the 5 steps so validation might fail because of not having the right value in the params. Because what you have updated is model attribute not params.

When you choose the self.attribute
You change at the 2nd step.
You go with flow, and all further steps will have updated value in the params.

I would suggest changing the contract/params instead of the model.

But just for sake of sharing I’m sharing the option of model as well.

In any case, model updates should be avoided in the populator.
But you just don’t have validation and don’t care about in between steps, you can go with the model update option.

Trailblazer is an awesome gem, you won’t like normal structure after that.

huangapple
  • 本文由 发表于 2023年8月10日 23:34:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76877266.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定