class StrsubcatRuntimeTables

В задачи класса входят:

* создавать
* уничтожать
* заполнять
* обновлять записи

таблиц вида `c80_yax_strcat_#{NN}_items".

Public Class Methods

check_and_clean_item_props(strsubcat) click to toggle source

удаляем из таблицы item_props те строки, у которых strsubcat_id == ID и чьё prop_name_id входит в список свойств LIST вызывается только в методе StrsubcatSweeper.after_update

# File lib/c80_yax/strsubcat_runtime_tables.rb, line 324
def self.check_and_clean_item_props(strsubcat) # strh_check_and_clean_item_props
  list = strsubcat.get_list_removed_props

  if list.count > 0
    Rails.logger.debug '[TRACE] <StrsubcatRuntimeTables.check_and_clean_item_props> Чистим item_props.'
    # 1.1. Сначала составим список свойств, которые надо удалить.

    sql = "
    SELECT
      `c80_yax_item_props`.`id`,
      `c80_yax_item_props`.`prop_name_id`,
      `c80_yax_prop_names`.`title` AS prop_name_title,
      `c80_yax_item_props`.`value`,
      `c80_yax_items`.`id` AS item_id,
      `c80_yax_items`.`title` as item_title,
      `c80_yax_items`.`is_main`,
      `c80_yax_items`.`is_hit`,
      `c80_yax_items`.`is_sale`,
      `c80_yax_strsubcats`.`id` AS strsubcat_id,
      `c80_yax_strsubcats`.`slug` AS strsubcat_slug
    FROM `c80_yax_items`
      INNER JOIN `c80_yax_strsubcats` ON `c80_yax_strsubcats`.`id` = `c80_yax_items`.`strsubcat_id`
      LEFT JOIN `c80_yax_item_props` ON `c80_yax_item_props`.`item_id` = `c80_yax_items`.`id`
      LEFT JOIN `c80_yax_prop_names` ON `c80_yax_prop_names`.`id` = `c80_yax_item_props`.`prop_name_id`
    WHERE `c80_yax_strsubcats`.`id` = #{ strsubcat.id }
    AND `prop_name_id` IN (#{ list.join(',')})
  "

    records = ActiveRecord::Base.connection.execute(sql)

    # 1.2. Составим список айдишников свойств для удаления
    item_props_ids = []
    records.each do |row|
      item_props_ids << row[0]
    end

    # 2. Теперь на основе списка свойств, которые надо удалить, составим запрос, который удалит эти свойства.
    if item_props_ids.count > 0
      sql = "
    DELETE FROM `c80_yax_item_props`
    WHERE `id` IN (#{ item_props_ids.join(',') })
  "

      ActiveRecord::Base.connection.execute(sql)
    end

    Rails.logger.debug '[TRACE] <StrsubcatRuntimeTables.check_and_clean_item_props> END'
  end

end
item_drop(strsubcat_id, item_id) click to toggle source

из таблицы типа strcat_111_items удалить строку, описывающую вещь item_id

# File lib/c80_yax/strsubcat_runtime_tables.rb, line 376
def self.item_drop(strsubcat_id, item_id) # strh_item_drop
  runtime_table_name = "c80_yax_strcat_#{strsubcat_id}_items"
  Rails.logger.debug "[TRACE] <StrsubcatRuntimeTables.item_drop> Удаляем вещь id = #{item_id} из таблицы `#{runtime_table_name}`."
  if ActiveRecord::Base.connection.table_exists?(runtime_table_name)
    sql = "
    DELETE FROM #{runtime_table_name}
    WHERE item_id = #{item_id}
    "

    ActiveRecord::Base.connection.execute(sql)
  end
end
item_update(strsubcat_id,item_id) click to toggle source

в таблице типа strcat_111_items обновить поля строки, описывающую вещь item_id

# File lib/c80_yax/strsubcat_runtime_tables.rb, line 390
def self.item_update(strsubcat_id,item_id) # strh_item_update
  runtime_table_name = "c80_yax_strcat_#{strsubcat_id}_items"
  Rails.logger.debug "[TRACE] <StrsubcatRuntimeTables.item_update> В таблице `#{runtime_table_name}` обновим данные о товаре id = #{item_id}."

  # <editor-fold desc="# сначала соберём все свойства вещи">
  # NOTE:: [columns] эти столбцы должны соответствовать (запрос при обновлении предмета)
  sql = "
        SELECT
          `c80_yax_item_props`.`prop_name_id`,
          `c80_yax_prop_names`.`title` AS prop_name_title,
          `c80_yax_item_props`.`value`,
          `c80_yax_items`.`id` AS item_id,
          `c80_yax_items`.`title` as item_title,
          `c80_yax_items`.`is_main`,
          `c80_yax_items`.`is_hit`,
          `c80_yax_items`.`is_sale`,
          `c80_yax_strsubcats`.`id` AS strsubcat_id,
          `c80_yax_strsubcats`.`slug` AS strsubcat_slug,
          `c80_yax_vendors`.`id` as vendor_id,
          `c80_yax_vendors`.`title` as vendor_title,
          `c80_yax_items`.`full_desc`,
          `c80_yax_items`.`image`,
          `c80_yax_items`.`is_ask_price`,
          `c80_yax_items`.`is_gift`,
          `c80_yax_items`.`is_starting`,
          `c80_yax_items`.`is_available`
        FROM `c80_yax_items`
          INNER JOIN `c80_yax_strsubcats` ON `c80_yax_strsubcats`.`id` = `c80_yax_items`.`strsubcat_id`
          LEFT JOIN `c80_yax_item_props` ON `c80_yax_item_props`.`item_id` = `c80_yax_items`.`id`
          LEFT JOIN `c80_yax_prop_names` ON `c80_yax_prop_names`.`id` = `c80_yax_item_props`.`prop_name_id`
          LEFT JOIN `c80_yax_items_vendors` ON `c80_yax_items_vendors`.`item_id` = `c80_yax_items`.`id`
          LEFT JOIN `c80_yax_vendors` ON `c80_yax_vendors`.`id` = `c80_yax_items_vendors`.`vendor_id`
        WHERE (`c80_yax_strsubcats`.`id` = #{strsubcat_id})
        AND `c80_yax_items`.`id` = #{item_id};
  "
  records = ActiveRecord::Base.connection.execute(sql)
  # </editor-fold>

  # составим SQL команду
  hash_sql = self.hash_sql_make(records, strsubcat_id)

  #-> удалим старую строку
  sql = "DELETE FROM `#{runtime_table_name}` WHERE item_id = #{item_id}"
  ActiveRecord::Base.connection.execute(sql)

  #-> выполним SQL команду : вставим строку с обновлёнными значениями
  self.hash_sql_execute(hash_sql)

  Rails.logger.debug '[TRACE] <StrsubcatRuntimeTables.strh_item_update> END'
end
table_check_and_build(strsubcat) click to toggle source

создаёт таблицу под все вещи подкатегории, если такой таблицы нету в базе

# File lib/c80_yax/strsubcat_runtime_tables.rb, line 201
def self.table_check_and_build(strsubcat) # strh_table_check_and_build

  # по-умолчанию в результате исполнения метода - таблицу создавать не надо
  # noinspection RubyUnusedLocalVariable
  mark_create_table = false

  runtime_table_name = "c80_yax_strcat_#{strsubcat.id}_items"

  # <editor-fold desc="# Сначала составим список айдишников всех свойств, присущих данной категории">
  # выбрать из базы айдишники всех свойств, присущих данной категории
  # по сути дела, тоже самое, что и strsubcat.prop_names
  sql = "
      SELECT `c80_yax_prop_names`.`id`
      FROM `c80_yax_prop_names`
      INNER JOIN `c80_yax_prop_names_strsubcats` ON `c80_yax_prop_names`.`id` = `c80_yax_prop_names_strsubcats`.`prop_name_id`
      WHERE `c80_yax_prop_names_strsubcats`.`strsubcat_id` = #{strsubcat.id}
  "
  records = ActiveRecord::Base.connection.execute(sql)

  propnames_ids = []
  records.each do |r|
    propnames_ids << r[0].to_i
  end
  # </editor-fold>

  # Теперь определимся, нужно ли дропнуть и создать таблицу
  if ActiveRecord::Base.connection.table_exists? runtime_table_name
    Rails.logger.debug "[TRACE] <StrsubcatRuntimeTables.table_check_and_build> Таблица уже существует: #{runtime_table_name}"

    # <editor-fold desc="# совпадают ли столбцы runtime таблицы и свойства?">
    # проверим, совпадают ли столбцы свойств в runtime таблице со списком свойств, присущих данной категории:
    # если какого-то свойства нет в столбцах - дропнем таблицу и создадим её заново
    # если есть лишний столбец в таблице - дропнем таблицу и создадим её заново
    Rails.logger.debug '[TRACE] <StrsubcatRuntimeTables.table_check_and_build> Проверим соответствие столбцов свойствам.'

    # получим список столбцов и извлечём из него айдишники свойств в массив +actual_props_ids+
    sql_describe = "DESCRIBE #{runtime_table_name}"
    records = ActiveRecord::Base.connection.execute(sql_describe)
    actual_props_ids = []
    records.each do |row|
      actual_props_ids << row[0][/\d+/].to_i if row[0]['prop_']
    end

    mark_create_table = actual_props_ids.sort != propnames_ids.sort
    Rails.logger.debug "[TRACE] <StrsubcatRuntimeTables.table_check_and_build> Сравним propnames_ids = #{propnames_ids} и actual_props_ids = #{actual_props_ids}."
    Rails.logger.debug "[TRACE] <StrsubcatRuntimeTables.table_check_and_build> mark_create_table = #{mark_create_table}."
    # </editor-fold>

    # если не совпадают - дропнем таблицу
    if mark_create_table
      self.table_drop(strsubcat)
    end

  else
    Rails.logger.debug "[TRACE] <check_and_build_table> Таблица не существует, создаём: #{runtime_table_name}"
    mark_create_table = true
  end

  # Если таблицу надо создать - создаём
  if mark_create_table
    self.table_create(runtime_table_name, propnames_ids)
  end

end
table_create(runtime_table_name, propnames_ids) click to toggle source

враппер метода ActiveRecord::Migration.create_table создаёт таблицу с именем strcat_#{strsubcat_id}_items со столбцами, описанными в массиве props_names_list (имя столбца вида prop_#{item})

# File lib/c80_yax/strsubcat_runtime_tables.rb, line 269
def self.table_create(runtime_table_name, propnames_ids) # strh_table_create
  Rails.logger.debug "[TRACE] <StrsubcatRuntimeTables.table_create> BEGIN. propnames_ids = #{propnames_ids}"

  ActiveRecord::Migration.create_table(runtime_table_name, :options => 'COLLATE=utf8_unicode_ci') do |t|

    # NOTE:: [columns] эти столбцы должны соответствовать (создание новой таблицы)

    t.integer :item_id
    t.string :item_title
    t.boolean :is_main
    t.boolean :is_hit
    t.boolean :is_sale
    t.integer :strsubcat_id
    t.string :strsubcat_slug
    t.integer :vendor_id
    t.text :full_desc
    t.string :image
    t.boolean :is_ask_price
    t.boolean :is_gift
    t.boolean :is_starting
    t.boolean :is_available

    t.index :item_id, unique: true

    # т.к. сортировка средствами SQL (ORDER BY) нам
    # нужна только для цен, то только столбцы для
    # цен делаем числами. Остальное - храним, как строки.
    propnames_ids.each do |prop_name_id|
      is_decimal_column = C80Yax::PropName.find(prop_name_id).is_normal_price
      if is_decimal_column
        t.decimal "prop_#{prop_name_id}", :precision => 8, :scale => 2
      else
        t.string "prop_#{prop_name_id}"
      end

    end

  end

  Rails.logger.debug '[TRACE] <StrsubcatRuntimeTables.table_create> END'

end
table_drop(strsubcat) click to toggle source
# File lib/c80_yax/strsubcat_runtime_tables.rb, line 312
def self.table_drop(strsubcat) # strh_table_drop
  runtime_table_name = "c80_yax_strcat_#{strsubcat.id}_items"
  if ActiveRecord::Base.connection.table_exists?(runtime_table_name)
    Rails.logger.debug "[TRACE] <StrsubcatRuntimeTables.table_drop> Удаляем таблицу: table_name = #{runtime_table_name}"
    ActiveRecord::Migration.drop_table(runtime_table_name)
    Rails.logger.debug '[TRACE] <StrsubcatRuntimeTables.table_drop> END'
  end
end
table_fill(strsubcat_id) click to toggle source
# File lib/c80_yax/strsubcat_runtime_tables.rb, line 12
def self.table_fill(strsubcat_id) # strh_table_fill

  # NOTE:: [columns] эти столбцы должны соответствовать (комментарий как wiki)

  # [0] item_props.prop_name_id,
  # [1] prop_names.title AS prop_name_title,
  # [2] item_props.value,
  # [3] items.id AS item_id,
  # [4] items.title as item_title,
  # [5] items.is_main,
  # [6] items.is_hit,
  # [7] items.is_sale,
  # [8] strsubcats.id AS strsubcat_id,
  # [9] strsubcats.slug AS strsubcat_slug
  # [10] vendors.id as vendor_id
  # [11] vendors.title as vendor_title - дублирующий столбец: чтобы не совершать
  #                                      лишних запросов за vendor.title в методе hash_sql_make
  # [12] items.full_desc
  # [13] items.image
  # [14] items.is_ask_price
  # [15] `c80_yax_items`.`is_gift`,
  # [16] `c80_yax_items`.`is_starting`,
  # [17] `c80_yax_items`.`is_available`
  # выбираем свойства предметов вместе с инфой о предметах, которым они принадлежат

  # <editor-fold desc="# составляем и выполняем sql запрос">

  # NOTE:: [columns] эти столбцы должны соответствовать (запрос при заполнении таблицы)

  sql = "
        SELECT
          `c80_yax_item_props`.`prop_name_id`,
          `c80_yax_prop_names`.`title` AS prop_name_title,
          `c80_yax_item_props`.`value`,
          `c80_yax_items`.`id`         AS item_id,
          `c80_yax_items`.`title`      AS item_title,
          `c80_yax_items`.`is_main`,
          `c80_yax_items`.`is_hit`,
          `c80_yax_items`.`is_sale`,
          `c80_yax_strsubcats`.`id`    AS strsubcat_id,
          `c80_yax_strsubcats`.`slug`  AS strsubcat_slug,
          `c80_yax_vendors`.`id`       AS vendor_id,
          `c80_yax_vendors`.`title`    AS vendor_title,
          `c80_yax_items`.`full_desc`,
          `c80_yax_items`.`image`,
          `c80_yax_items`.`is_ask_price`,
          `c80_yax_items`.`is_gift`,
          `c80_yax_items`.`is_starting`,
          `c80_yax_items`.`is_available`
        FROM `c80_yax_items`
          INNER JOIN `c80_yax_strsubcats` ON `c80_yax_strsubcats`.`id` = `c80_yax_items`.`strsubcat_id`
          LEFT JOIN `c80_yax_item_props` ON `c80_yax_item_props`.`item_id` = `c80_yax_items`.`id`
          LEFT JOIN `c80_yax_prop_names` ON `c80_yax_prop_names`.`id` = `c80_yax_item_props`.`prop_name_id`
          LEFT JOIN `c80_yax_items_vendors` ON `c80_yax_items_vendors`.`item_id` = `c80_yax_items`.`id`
          LEFT JOIN `c80_yax_vendors` ON `c80_yax_vendors`.`id` = `c80_yax_items_vendors`.`vendor_id`
        WHERE (`c80_yax_strsubcats`.`id` = #{strsubcat_id})
  "
    records = ActiveRecord::Base.connection.execute(sql)
    records
  # </editor-fold>

  # 1. заполняем хэш объектами для составления sql-команд INSERT
  hash_sql = self.hash_sql_make(records, strsubcat_id)

  Rails.logger.debug '[TRACE] <StrsubcatRuntimeTables.table_fill> Начинаем заполнять таблицу:'
  self.hash_sql_execute(hash_sql)
  Rails.logger.debug '[TRACE] <StrsubcatRuntimeTables.table_fill> END'

end

Private Class Methods

hash_sql_execute(hash_sql) click to toggle source
# File lib/c80_yax/strsubcat_runtime_tables.rb, line 633
def self.hash_sql_execute(hash_sql)

  # INSERT INTO Table1
  # (`id`, `name`)
  # VALUES
  #   (1, 'foo'),
  #   (1, 'bar'),
  #   (1, 'foobar'),
  #   (2, 'foo'),
  #   (2, 'bar'),
  #   (2, 'foobar')
  #;
  hash_sql.each_key do |key|
    sql = hash_sql[key][:sql]
    # Rails.logger.debug sql
    begin
      ActiveRecord::Base.connection.execute(sql)
    rescue Exception => e
      Rails.logger.debug "[TRACE] <StrsubcatRuntimeTables.hash_sql_execute> #{e}"
    end
  end
end
hash_sql_make(records, strsubcat_id) click to toggle source

# собираем все доступные значения для селекта def strh_collect_all_vals(prop_id,table_name)

# SELECT
# id,
# GROUP_CONCAT(prop_3   SEPARATOR '----') all_values_3
# FROM strcat_1_items;

s = 'SET SESSION group_concat_max_len = 102400000'
ActiveRecord::Base.connection.execute(s)

sql = "SELECT id, GROUP_CONCAT(prop_#{prop_id} SEPARATOR '----') all_values_#{prop_id} FROM #{table_name}"
records_array = ActiveRecord::Base.connection.exec_query(sql)
hsh = records_array.to_hash
# [{"id"=>1, "3"=>"95 г/м3,1450 кг/м3"}]

res = {}
hsh[0].each_key do |key|
  unless key["id"]
    res = hsh[0][key]
    break
  end
end

Rails.logger.debug "<strh_collect_all_vals> Cобрали все доступные значения для селекта id=#{prop_id}: res = #{res}"
res

end

# File lib/c80_yax/strsubcat_runtime_tables.rb, line 542
def self.hash_sql_make(records, strsubcat_id)
  Rails.logger.debug "[TRACE] <StrsubcatRuntimeTables.hash_sql_make> Составляем SQL для обновления/создания записи в runtime-таблице `c80_yax_strcat_#{strsubcat_id}_items`. records.count = #{records.count}."

  hash_sql = {}

  records.each do |item_prop|

    prop_name_id = item_prop[0]
    prop_value = item_prop[2]
    item_id = item_prop[3]
    is_ask_price = item_prop[14]

    # пригодится, если is_ask_price.to_i == 1
    # is_price = PropName.find(prop_name_id).is_normal_price # не подходит, т.к. нужна только "цена за шт"
    is_price = Proc.new { |pid|
      C80Yax::PropName.find(pid).is_normal_price
    }.call(prop_name_id)

    # <editor-fold desc="# производим магические манипуляции с Производителем">
    # если это свойство "бренд" -
    # то игнорируем значение, которое лежит в таблице itep_props
    vendor_id = item_prop[10]
    vendor_title = item_prop[11]
    if prop_name_id == 36 # NOTE-HARD-CODE-PROP-NAME
      prop_value = vendor_title
      if prop_value.blank?
        prop_value = 'Не указан'
      end
    end

    # корректируем значение
    unless vendor_id.present?
      vendor_id = -1
    end
    # </editor-fold>

    # <editor-fold desc="# манипулируем с is_ask_price">
    if is_ask_price.present?
      if is_price
        # если у товара is_ask_price = true, то игнорируем значение, которое лежит в таблице
        # item_props и в prop_value помещаем максимально возможное значение, чтобы товар, при сортировке
        # по цене, уходил в самый конец списка
        # 20170402: также опытным путём выяснилось, что надо также обновлять значение
        # в таблице items_props
        if is_ask_price.to_i == 1
          Rails.logger.debug '[TRACE] <StrsubcatRuntimeTables.hash_sql_make> is_ask_price=TRUE.'
          prop_value = 999999
          # и обновляем значение в таблице items_props
          item_prop_price = C80Yax::ItemProp.where(:item_id => item_id).where(:prop_name_id => prop_name_id)
          if item_prop_price.count > 0
            item_prop_price.first.update_column(:value, 999999)
          end
        end
      end
    else
      # защищаемся от ошибки "Mysql2::Error: Incorrect integer value: '' for column 'is_ask_price'"
      is_ask_price = 0
    end
    # </editor-fold>

    # кэшируем первый раз
    unless hash_sql[item_id].present?
     # Rails.logger.debug "\t new hash"
      hash_sql[item_id] = {
          prop_names_ids: %w'item_id item_title is_main is_hit is_sale strsubcat_id strsubcat_slug vendor_id full_desc image is_ask_price is_gift is_starting is_available',
          values: [item_id,item_prop[4],item_prop[5],item_prop[6],item_prop[7],item_prop[8],item_prop[9],vendor_id,item_prop[12],item_prop[13], is_ask_price, item_prop[15], item_prop[16], item_prop[17]],
          sql: "INSERT INTO `c80_yax_strcat_#{strsubcat_id}_items` ({props}) VALUES ({values})"
      }
    end

    # byebug
    Rails.logger.debug "[TRACE] <StrsubcatRuntimeTables.hash_sql_make> prop_name_id: #{prop_name_id}, prop_value: #{prop_value}."

    # добавляем в кэш
    hash_sql[item_id][:prop_names_ids] << "prop_#{prop_name_id}"
    hash_sql[item_id][:values] << prop_value

  end

  # 2. обходим этот хэш, для каждого объекта формируем sql-команду на основе данных объекта хэша
  hash_sql.each_key do |key|
    obj = hash_sql[key]
    p = "`#{obj[:prop_names_ids].join("`,`")}`"
    v = "'#{obj[:values].join("','")}'"
    obj[:sql] = obj[:sql].gsub(/\{props\}/,p)
    obj[:sql] = obj[:sql].gsub(/\{values\}/,v)
  end
  hash_sql

end

Private Instance Methods

is_only_one_filter_use(filter_params={}) click to toggle source
NOTE

этот метод вообще работает?

# File lib/c80_yax/strsubcat_runtime_tables.rb, line 488
def is_only_one_filter_use(filter_params={})

  c = filter_params.count
  key_filter_used = -1
  filter_params.each_key do |key|
    if filter_params[key].to_s["-1"]
      c -= 1
    else
      key_filter_used = key
    end
  end

  if c == 1
    only_one_filter_used = true
  else
    only_one_filter_used = false
    key_filter_used = -1
  end

  Rails.logger.debug "[TRACE] <StrItemsHelper.is_only_one_filter_use> filters in use = #{c}; only_one_filter_used = #{only_one_filter_used}, key_filter_used = #{key_filter_used}"
  key_filter_used

end