как избежать дубликатов в has_many: через отношения?

Я написал эту небольшую функцию несколько лет назад:

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

Это позволяет запускать операторы в однострочном C # -ish String.Format, например:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

Он избегает использования типа переменной. Если вы попытаетесь параметризовать имена таблиц и столбцов, это будет терпеть неудачу, поскольку она помещает каждую строку в кавычки, которая является недопустимым синтаксисом.

ОБНОВЛЕНИЕ БЕЗОПАСНОСТИ: предыдущая версия str_replace разрешала инъекции, добавляя токены {#} в пользовательские данные. Эта версия preg_replace_callback не вызывает проблем, если замена содержит эти токены.

30
задан zishe 27 May 2014 в 22:11
поделиться

4 ответа

Что относительно:

Blog.find(:all,
          :conditions => ['id NOT IN (?)', the_reader.blog_ids])

направляющие заботится о наборе идентификаторов для нас с методами ассоциации!:)

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

5
ответ дан Josh Delsman 27 November 2019 в 22:56
поделиться

Простое решение это встроено в направляющие:

 class Blog < ActiveRecord::Base
     has_many :blogs_readers, :dependent => :destroy
     has_many :readers, :through => :blogs_readers, :uniq => true
    end

    class Reader < ActiveRecord::Base
     has_many :blogs_readers, :dependent => :destroy
     has_many :blogs, :through => :blogs_readers, :uniq => true
    end

    class BlogsReaders < ActiveRecord::Base
      belongs_to :blog
      belongs_to :reader
    end

Примечание, добавляющее :uniq => true опция к эти has_many вызов.

Также Вы могли бы хотеть рассмотреть has_and_belongs_to_many между Блогом и Читателем, если у Вас нет некоторых других атрибутов, требуется иметь на модели соединения (который Вы не делаете, в настоящее время). Тот метод также имеет :uniq opiton.

Примечание, что это не препятствует тому, чтобы Вы создали записи в таблице, но она действительно гарантирует, чтобы при запросах набора Вы получили только один из каждого объекта.

Обновление

В направляющих 4 способ сделать это через блок объема. Вышеупомянутые изменения в.

class Blog < ActiveRecord::Base
 has_many :blogs_readers, dependent:  :destroy
 has_many :readers,  -> { uniq }, through: :blogs_readers
end

class Reader < ActiveRecord::Base
 has_many :blogs_readers, dependent: :destroy
 has_many :blogs, -> { uniq }, through: :blogs_readers
end

class BlogsReaders < ActiveRecord::Base
  belongs_to :blog
  belongs_to :reader
end

Обновление для направляющих 5

использование uniq в блоке объема вызовет ошибку NoMethodError: undefined method 'extensions' for []:Array. Используйте distinct вместо этого:

class Blog < ActiveRecord::Base
 has_many :blogs_readers, dependent:  :destroy
 has_many :readers,  -> { distinct }, through: :blogs_readers
end

class Reader < ActiveRecord::Base
 has_many :blogs_readers, dependent: :destroy
 has_many :blogs, -> { distinct }, through: :blogs_readers
end

class BlogsReaders < ActiveRecord::Base
  belongs_to :blog
  belongs_to :reader
end
82
ответ дан 3 revs, 3 users 44% 27 November 2019 в 22:56
поделиться

Это должно заботиться о Вашем первом вопросе:

class BlogsReaders < ActiveRecord::Base
  belongs_to :blog
  belongs_to :reader

  validates_uniqueness_of :reader_id, :scope => :blog_id
end
37
ответ дан Mike Breen 27 November 2019 в 22:56
поделиться

Я думаю, что кто-то приедет с лучшим ответом, чем это.

the_reader = Reader.find(:first, :include => :blogs)

Blog.find(:all, 
          :conditions => ['id NOT IN (?)', the_reader.blogs.map(&:id)])

[редактирование]

см. ответ Josh's ниже. Это - способ пойти. (Я знал, что там был лучший путь;)

1
ответ дан Mike Breen 27 November 2019 в 22:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: