shopqi源码分析二

12 篇文章 0 订阅

分析admin路径下的一系列模块

products

  • routes.rb:
      resources :products, except: :edit do
        collection do
          get :inventory
          post :set
        end
        member do
          put :update_published
          post :duplicate
        end

创建没有edit方法的products,新增

/products/inventory

/products/set

/products/1/update_published

/products/1/duplicate

四个路由

  • 从点击/shopqi/app/views/layouts/admin.html.haml中的“商品”菜单开始,/shopqi/app/controllers/admin/products_controller.rb

  def index
    @products_json = products.to_json({include: [:variants, :options], except: [:created_at, :updated_at],methods:[:index_photo]})
  end

跳转到index页面


由于没有商品,在页面中只有一个入口——new

现在回到products_controller来看new方法

  def new
    #保证至少有一个款式
    product.variants.build  if product.variants.empty?
  end
说的很明白了 直接跳到页面,准备填写新的product

现在让我们来分析product这个model

# encoding: utf-8
class Product < ActiveRecord::Base
  include Models::Handle       
导入module 位置:lib/models/handle.rb,include首先执行self.included(base)。 里面的方法有模块方法self.handleize;类方法handle!,published_handle!;实例方法make_valid。一共五个方法。
  belongs_to :shop
shop.rb中代码   has_many :products              , dependent: :destroy                      , order: 'id desc' 两者关系为一对多,如果shop没了,product也会消失,排序按照id 倒序
  has_many :photos                    , dependent: :destroy           , order: 'position asc'
photos 图片类  belongs_to :product 一对多关系 需要注意的是用到了 order position,可以从/shopqi/db/migrate/20110422070212_create_products.rb中看出 create_table :photos do |t|      t.references :product      t.string :product_image_uid      t.string :product_image_format      t.integer :position     , comment: '排序序号'      t.timestamps    end photo中有一个类型为integer的position字段 
  has_many :variants                  , dependent: :destroy           , class_name: 'ProductVariant'         , order: 'position asc'

ProductVariant,商品款式类 需要注意的是它的order:position并不是字段名,而是acts_as_list插件,在rails2中被剔除,然后已插件形式出现,该插件的目的是再model数据库存储中,在一对多关系中,将多端按照作为一个有顺序的列表来存储,并提供一些移动等方法来辅助,acts_as_list详细用法请移步这里 下面可以看到定义的variants类中的代码 

 class ProductVariant < ActiveRecord::Base 

     .......

     acts_as_list scope: :product

     .......

  has_many :options                   , dependent: :destroy           , class_name: 'ProductOption'          , order: 'position asc'
options 产品选项类
  has_many :custom_collection_products, dependent: :destroy           , class_name: 'CustomCollectionProduct'
集合关联的商品 中间表 在/shopqi/app/models/custom_collection.rb中
  has_many :custom_collections        , class_name: 'CustomCollection', through: :custom_collection_products , source: :custom_collection
custom_collection_products是一个中间表,由我们自己定义而得。由它把product和custom_collections连接起来。source: 是跟在through:后面的精确判定,用于告诉rails到哪里去找custom_collection_products。
  has_many :smart_collection_products , dependent: :destroy           , class_name: 'SmartCollectionProduct'
  has_and_belongs_to_many :tags       , order: 'id asc'
  # 标签
  attr_accessor :tags_text,:images
列出了两个属性的getset方法
  attr_accessible :handle, :title, :published, :body_html, :price, :product_type, :vendor, :tags_text, :images, :photos_attributes, :variants_attributes, :options_attributes, :custom_collection_ids
属性白名单,这些都可以直接写入数据库
  scope :published, where(published: true)
定义了一个可直接调用的方法,published
  accepts_nested_attributes_for :photos  , allow_destroy: true
  accepts_nested_attributes_for :variants, allow_destroy: true
  accepts_nested_attributes_for :options, allow_destroy: true
accepts_nested_attributes_for,当创建一个product时,就创建一个photo,一个variants,一个option。生成写方法:photos_attributes,variants_attributes,options_attributes.此属性解释详见这里

  validates_presence_of :title, :product_type, :vendor
必须填写这三项
  #商品列表中显示的产品图片
  def index_photo
    photo('thumb')
  end
显示缺省图片
  def photo(version = :icon)
    unless photos.empty?
      photos.first.send(version)
    else
      "/assets/admin/no-image-#{version}.gif"
    end
  end

这边需要讲解一下了,index_photo方法调用了photo方法,传入了缺省值thumb,如果数据库中有图片,则取出第一张。

photos.first.send(version),调用了ruby的send方法,用一个符号表示方法(具体send的用法请点击这里

下面让我们来看看photos模块中,到底是如何定义.send调用的方法的。

让我们来到photos模块,以下是photos模块的所有代码:

class Photo < ActiveRecord::Base
  belongs_to :product
  default_scope order: 'position asc'
  attr_accessible :product_image, :position
  VERSION_KEYS = []  


  image_accessor :product_image do    
  image_accessor是插件dragonfly定义的一个方法,从字面意思就可理解:为product_image定义geter/setter方法
    storage_path{ |image|                               存储路径
      "#{self.product.shop.id}/products/#{self.product.id}/#{image.basename}_#{rand(1000)}.#{image.format}" # data/shops/1/products/1/foo_45.jpg
    }
  end


  validates_size_of :product_image, maximum: 8000.kilobytes
  validates_with StorageValidator                 validates_with,指定另一个类来构建更为复杂的验证

StorageValidator 代码如下:

class StorageValidator < ActiveModel::Validator #验证容量是否超过商店限制



  # 需要限制的地方:

  # 1. 商品图片上传

  # 2. 富文本框的图片上传

  # 3. 主题上传、复制和安装(直接判断)

  # 4. 附件上传(直接判断)

  # 5. 主题设置中的图片上传(直接判断)

  def validate(record)

    record.errors[:base] << I18n.t('activerecord.errors.models.shop.attributes.storage.full') unless record.shop.nil? or record.shop.storage_idle?

  end



end



  validates_property :mime_type, of: :product_image, in: %w(image/jpeg image/jpg image/png image/gif), message:  "格式不正确"


  #定义图片显示大小种类
  def self.versions(opt={})
    opt.each_pair do |k,v|
      VERSION_KEYS << k
      define_method k do
        if product_image
          product_image.thumb(v).url      先用imagemagick进行剪切后,给出url
        end
      end
    end
  end


  def shop # 直接使用delegate :shop, to: :product在新增商品带图片的情况下会报500错误 #416
    product ? product.shop : nil
  end


  #显示在产品列表的缩略图(icon)
  #后台管理商品详情(small)
  versions pico: '16x16', icon: '32x32', thumb: '50x50', small:'100x100', compact: '160x160', medium: '240x240', large: '480x480', grande: '600x600', original: '1024x1024'


end

从以上代码,photo类定义了类方法versions,在最后又带入参数执行了它,把key值传给了VERSION_KEYS,随后以key值的名字作为方法名,定义了一系列的方法,内容都为:方法名为key,如果product_image存在,那么返回product_image的url。

好了,现在我们就能理解photos.first.send(version),调用了photos类里面的pico|icon|thumb|small|compact|.....方法,返回一个图片的url。

需要说明的一点是,photo中运用了dragonfly插件,这里是此插件官网,请自行察看。

  searchable do
    integer :shop_id, references: Shop
    text :title, :body_html, :product_type, :vendor
    text :variants_text do
      variants.map do |variant|
        [variant.option1, variant.option2, variant.option3, variant.sku]
      end
    end
  end
sunspot插件,solr搜索引擎

后面代码就不解释了,大同小异比较简单,其它关注点:

product类里面还包含了ProductVariant(产品款式)和ProductOption(产品选项)类。

这里是default_scope的一些用法:

default_scope
default_scope(scope = {}) protected
Use this macro in your model to set a default scope for all operations on the model.


class Article < ActiveRecord::Base
  default_scope where(:published => true)
end


Article.all # => SELECT * FROM articles WHERE published = true
The default_scope is also applied while creating/building a record. It is not applied while updating a record.


Article.new.published    # => true
Article.create.published # => true

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值