Я написал эту небольшую функцию несколько лет назад:
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
не вызывает проблем, если замена содержит эти токены.
Что относительно:
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blog_ids])
направляющие заботится о наборе идентификаторов для нас с методами ассоциации!:)
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Простое решение это встроено в направляющие:
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
Это должно заботиться о Вашем первом вопросе:
class BlogsReaders < ActiveRecord::Base
belongs_to :blog
belongs_to :reader
validates_uniqueness_of :reader_id, :scope => :blog_id
end
Я думаю, что кто-то приедет с лучшим ответом, чем это.
the_reader = Reader.find(:first, :include => :blogs)
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blogs.map(&:id)])
[редактирование]
см. ответ Josh's ниже. Это - способ пойти. (Я знал, что там был лучший путь;)