Создавайте и открывайте новые связанные объекты без вставки

Создавайте и открывайте новые связанные объекты без вставки

Без использования базы данных я хотел бы создать новый объект (песню) DBIx :: Class и присоединить к нему несколько объектов (тегов), связанных с несколькими объектами. Тогда я хотел бы получить доступ к тегам таким образом:

my @tags = $song->tags();

Я уже могу это сделать, если сначала вставлю объекты в базу данных, но как это сделать без использования insert ()?

Ниже мой код, который выводит имя созданного тега. Теперь я бы хотел добиться этого без использования вставки.

Это всего лишь тестовый код. В противном случае у меня есть более сложная схема, которая используется с MySql. Теперь я также использую Solr и хочу преобразовать ответ Solr в объекты DBIx :: Class (без доступа к db).

 #!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;

package MySchema::Song;
use base 'DBIx::Class';
__PACKAGE__->load_components('Core');
__PACKAGE__->table("artist");
__PACKAGE__->add_columns(
    id   => { data_type => 'integer', is_nullable => 0, is_auto_increment => 1 },
    name => { data_type => 'varchar', is_nullable => 1, },
);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many('song_tag' => 'MySchema::SongTag', 'song_id');
__PACKAGE__->many_to_many('tags' => 'song_tag', 'tag');

package MySchema::Tag;
use base 'DBIx::Class';
__PACKAGE__->load_components('Core');
__PACKAGE__->table("tag");
__PACKAGE__->add_columns(
    id   => { data_type => 'integer', is_nullable => 0, is_auto_increment => 1 },
    name => { data_type => 'varchar', is_nullable => 1, },
);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many('song_tag' => 'MySchema::SongTag', 'tag_id');
__PACKAGE__->many_to_many('songs' => 'song_tag', 'song');

package MySchema::SongTag;
use base 'DBIx::Class';
__PACKAGE__->load_components('Core');
__PACKAGE__->table("song_tag");
__PACKAGE__->add_columns(
    song_id => { data_type => 'integer', is_nullable => 0 },
    tag_id  => { data_type => 'integer', is_nullable => 0 },
);
__PACKAGE__->set_primary_key( qw(song_id tag_id) );
__PACKAGE__->belongs_to('song' => 'MySchema::Song','song_id');
__PACKAGE__->belongs_to('tag' => 'MySchema::Tag', 'tag_id');


package MySchema;
use base 'DBIx::Class::Schema';
__PACKAGE__->load_classes(qw(Song Tag SongTag));


package main;

my $schema;
$schema = MySchema->connect('dbi:SQLite:dbname=:memory:');
$schema->deploy;

my $song = $schema->resultset('Song')->new_result({ id => 1, name => 'Song name' });
my $tag = $schema->resultset('Tag')->new_result({ id => 1, name => 'Tag name' });

my $song_tag = $schema->resultset('SongTag')->new({
    song => $song,
    tag => $tag,
});

$song->add_to_tags($tag);
#$tag->add_to_songs($song); # one or another

$song->insert;
$tag->insert;

# This works if objects are previously inserted
foreach my $tag ($song->tags) {
    print $tag->name."\n";
}

1;
  
Показать лучший ответ

Создание строки SongTag - это в основном то, что add_to_tags / songs делает под капотом, поэтому вам нужно либо или. Прочтите документацию DBIx :: Class о сгенерированных вспомогательных методах для отношений и о том, что они делают.

Если отладка включена, похоже, что $song->add_to_tags({ ... }) вызывает запись в базу данных, а $song->tags() вызывает чтение из базы данных. Я подумал, что может быть способ установить отношения без доступа к db, потому что, когда песни извлекаются из db вместе с тегами с использованием предварительной выборки, тогда нет доступа к db при использовании $song->tags() .

Я нашел решение, которое не очень хорошее, но оно работает без доступа к базе данных. Для этого объекты SongTag и Tag необходимо кэшировать внутри объекта Song, иначе $song->tags() получит доступ к базе данных.

 package main;

my $schema;
$schema = MySchema->connect('dbi:SQLite:dbname=:memory:');
$schema->deploy;

$schema->storage->debug(1);

my $song = $schema->resultset('Song')->new_result({ id => 1, name => 'Song name' });
my $tag = $schema->resultset('Tag')->new_result({ id => 98, name => 'Tag name' });
my $tag2 = $schema->resultset('Tag')->new_result({ id => 99, name => 'Tag name 2' });

# create many-to-many relations in memory without accessing database

my $rel = $schema->resultset('SongTag')->search(); # cretes empty resultset object

$rel->{related_resultsets}->{tag} = $schema->resultset('Tag')->search();
$rel->{related_resultsets}->{tag}->set_cache([ $tag, $tag2 ]);

$song->{related_resultsets}->{song_tag} = $schema->resultset('SongTag')->search();
$song->{related_resultsets}->{song_tag}->set_cache([ $rel ]);

foreach my $tag ($song->tags) {
    print $tag->name."\n";
}

1;
  

Вы никогда не должны обращаться к частным атрибутам, потому что обновление нарушит ваш код! set_cache - правильный способ сделать это, но api позволяет только заполнять наборы результатов. Пожалуйста, объясните, чего вы пытаетесь достичь.

Я знаю, что обновление модуля может сломать мое приложение, однако это единственный способ заставить его работать без базы данных. Как упоминалось выше, мне нужно составить $ song с тегами $ таким образом, чтобы теги $ song-> не имели доступа к db.

Интересно, почему вы используете ORM, если не хотите получать доступ к базе данных?