How do I get values from the link table in DBIx::Class? - perl

In my app I have 2 table, books and tags, and the link table book_tags. The link table also contains the number of times the book was tagged with this particular tag. I can add a tag by doing
$book->add_tag($tag, { tag_count => 10 });
However when I retrieve the tags for a book
#tags = $book->tags();
it does not seem to return the values from the link table.
Is there a way to get the values from the link table without doing it by hand?

Since you have a join table named book_tags, you will have to create a many_to_many relationship from the books table to the has_many relation of the books table itself. The has_many relation should retrieve the ids of the tags associated with the book from the table book_tags. You may have to add relationships similar to:
In Your::App::Schema::Result::Book:
__PACKAGE__->has_many( book_tags => 'Your::App::Schema::Result::BookTag', 'book_id' );
__PACKAGE__->many_to_many( tags => 'book_tags', 'tag' );
In Your::App::Schema::Result::BookTag:
__PACKAGE__->belongs_to( tag => 'Your::App::Schema::Result::Tag', 'tag_id' );

You can proxy properties through relationships like this:
Schema::Tag->has_many('book_tags' => 'Schema::BookTag',
undef, {
proxy => [ 'tag_count' ],
}
);
Then you can access 'tag_count' like this:
my $tag_rs = $book->tags;
for( my $tag = $tag_rs->next ){
print $tag->tag_count;
}
You can find more at the relationship docs.

Thanks! I have already done this part, and it works.
My tables look like this:
BOOK:
book_id book_title [other fields]
TAG:
tag_id tag_name [other fields]
BOOK_TAG:
book_id tag_id tag_count
So I have a many-to-many relationship between tags and books with an additional attribute, tag_count, which is stored in the link table.
I can add a tag to a book by doing
$book->add_to_tags($tag, { tag_count => 10 } );
which populates the tag_count field in the BOOK_TAG table.
But when I do
$book->tags();
it does not automatically retrieve tag_count field from BOOK_TAG. I can write
$tag = $schema->resultset('BookTag')->find( { book_id=>$book->book_id, tag=>$tag->tag_id });
$tag_count = $tag->tag_count();
I'm trying to see if there is an easier way to get the extra attribute from the link table.

Related

DbiX::Class / Creating tree of test data without persisting

I'm using DBIx::Class as or mapper for my Perl project. When it comes to generating test data I'm using DBIx::Class::ResultSet::new to create new entities in memory. In order to link entities with relationships I use set_from_related. This works absolutely flawless until I try to set the value(s) for a has_many relationship. Pseudo example:
# Table 'AUTHOR' has
# one-to-one (belongs_to) relationship named 'country' to table 'COUNTRY'
# one-to-many (has_many) relationship named 'books' to table 'BOOK'
my $s = Schema::getSchema();
my $author = $s->resultset('Author')->new({ name => 'Jon Doe', year_of_birth => 1982 });
my $country = $s->resultset('Country')->new({ name => 'Germany', iso_3166_code => 'DE' });
my $book = $s->resultset('Book')->new({ title => 'A star far away', publishing_year => 2002 });
# Now let's make 'em known to each other
$author->set_from_related('country', $country);
$author->set_from_related('books', $book);
# At this point
# $author->country is defined
# $author->books->first is undef <<<---- Problem
I cannot find a suitable method in the DBIx::Class::Relationship::Base documentation. The one closest to what I need is add_to_$rel but this method creates (persists) the entities. This is not an option for me as some of the entites used in my project don't belong to me (no write permission).
Does anyone have an idea how to add entities in memory for a has_many relationship ?
That's not possible as newly created result objects don't have their primary key column(s) populated until they are persisted to the database.
What you possibly want is to use multi-create and run that inside a transaction.

how to select particular column from relationship collection table in laravel mongodb jessenger

I have 3 columns in my database. 2 columns is connected with one column by using a hybrid relationship.
here is my query.
$data=Client::with('product','department')->select(['product.product_name','product.product_description']);
how to select row from another table?
in your product or department relation method do the selection with all the forging keys if you have any other relation for product for later use is you want them like
public function product()
{
return $this->hasMany(department::class)->select(['id', 'another_relation_to_product_id', 'product_name', 'product_description']);
}
You can do it this way
$data = Client::with(['product:id,product_name,product_description','department'])->get();
see docs https://laravel.com/docs/5.7/eloquent-relationships#constraining-eager-loads in Eager Loading Specific Columns section. Or you can do it
App\User::with([
'product' => function ($query) {
$query->select('id', 'product_name', 'product_description');
},
'department'
])->get();

How to create inline-records (IRRE) using DataHandler in TYPO3?

In our case we have a table A which contains IRRE records using table B. Inside a backend module we import an XML file to import those records for table B.
All records/data for table A is available.
All data for table B is available, except the new uids/identifiers.
Based on https://docs.typo3.org/typo3cms/CoreApiReference/6.2/ApiOverview/Typo3CoreEngine/Database/ I have to set the identifier NEWxxxx for all new created records.
I'm importing a large number of records at once. Can I generate those identifiers in a loop and process all records at once or do I have to run the whole datamap-handling record by record ?
Besides the identifier, is there any field i have to set on the parent record which contains the IRRE record ?
No translations/workspaces/other relations are involved.
Thanks for your help.
The DataHandler in TYPO3 is using the following array structure to create new or update existing records - this is valid up to and including TYPO3 CMS 8:
$dataMap = ['<table-name>' => [
'<record-uid>' => ['<field-name>' => '<field-value>']
];
Existing records use the integer value of the record's uid field, e.g. 123, new records use some random but unique identifier that are prefixed with NEW, e.g. NEWa2b3c4f8 created by uniqid('NEW', true) - since TYPO3 CMS 7 StringUtility::getUniqueId('NEW') can and should be used for that.
Generic Example
Let's assume the following records shall be created:
a new content element in table tt_content
two new inline file references for table sys_file_reference for field tt_content.image
referencing to existing sys_file record with uid 123
referencing to existing sys_file record with uid 234
// generating unique identifiers for records to be created
$ttContentId = 'NEW58d5079c8741c822627844'; // StringUtility::getUniqueId('NEW')
$fileRefId1st = 'NEW58d506f3cd0c4159344142'; // StringUtility::getUniqueId('NEW')
$fileRefId2nd = 'NEW58d50714c1226092562338'; // StringUtility::getUniqueId('NEW')
Preparing data-map
Hava a close look to tt_content.image, this is actually defining the (new) inline references, defined by a comma separated values of new records or existing records - this could either be NEWabc,NEWdef, 123,234,345 or NEWabc,123,NEWdef, mixing new and existing record references.
$dataMap = [
'tt_content' => [
'NEW58d5079c8741c822627844' => [
'title' => 'My new content element',
'bodytext' => 'Look at the following images...',
'CType' => 'textpic',
// $fileRefId1st & $fileRefId2nd, the sorting order is defined by this as well
'image' => 'NEW58d506f3cd0c4159344142,NEW58d50714c1226092562338',
],
],
'sys_file_reference' => [
'NEW58d506f3cd0c4159344142' => [
'uid_local' => 123,
'title' => 'Image #123',
],
'NEW58d50714c1226092562338' => [
'uid_local' => 234,
'title' => 'Image #234',
],
]
];
Preparing command-map
// the command-maps is similar to the data-map to copy, localize, move records
// however, it's not required in this scenario and thus stays empty
$commandMap = [];
Executing DataHandler
$dataHandler = new \TYPO3\CMS\Core\DataHandling\DataHandler();
$dataHandler->start($dataMap, $commandMap);
$dataHandler->process_datamap();
// $dataHandler->process_cmdmap(); // if $commandMap should be processed as well
If you need the uid of the created records, this can be resolved from the internal DataHandler record mapping. For example, the following code resolves the new uid of the created tt_content record:
// fetching the actual record ID, e.g. results in 333
$ttContentId = $dataHandler->substNEWwithIDs['NEW58d5079c8741c822627844'];
Notes
Defining the references happens in the example above directly for the field tt_content.image, which can contain NEW... ids as well as existing integer ids. The behaviour for the is the same for all reference types in TYPO3:
TCA type inline, for all variants (plain, foreign_field, MM)
TCA type select, for all variants (plain, MM)
TCA type group, for all variants (plain, MM)
Passing data through DataHandler ensures that log entries are created, and the in most cases modifications can be reverted using TYPO3's history/rollback module.
Besides that, it's possible to execute mass actions - the invocation of DataHandler is not limited to just on aggregate (the tt_content record in the example above). However, the NEW... ids have to be unique and must not be re-used during mass-executions to avoid side-effects.
Transformed to table_a & table_b scenario
Transforming this to the table_a and table_b scenario of the initial question, the $dataMap might look like the following. Of course you have to determine which references to table_b are bound to table_a.
$dataMap = [
// existing records of table_a, thus using the real ids
'table_a' => [
'11' => [ 'reference_field' => 'NEWb1,NEWb2' ],
'22' => [ 'reference_field' => 'NEWb3,NEWb4' ],
'33' => [ 'reference_field' => 'NEWb5,NEWb6' ],
],
// new records to be references for table_b, thus using NEW... ids
'table_b' => [
'NEWb1' => [ ... field values of this particular table_b record ... ],
'NEWb2' => [ ... field values of this particular table_b record ... ],
'NEWb3' => [ ... field values of this particular table_b record ... ],
'NEWb4' => [ ... field values of this particular table_b record ... ],
'NEWb5' => [ ... field values of this particular table_b record ... ],
'NEWb6' => [ ... field values of this particular table_b record ... ],
],
];
Notes on identifiers
Identifiers like NEWb1 are kept simple intentionally - usually those identifiers are composed by prefix NEW and a (pseudo-)random hexadecimal string abdc....
The TYPO3 core is using $id = StringUtility::getUniqueId('NEW') to create those unique identifiers. However, that also can be achieved using $id = 'NEW' . bin2hex(random_bytes(10); - identifiers just have to be unique for this particular process.

Associations with combination foreign key in Sails.js/Waterline

I need to have a model called 'Package' which can have one or more children of several different types. For example, a package can contain guides, as well as forms and other content types (some of which will be added later). Each of these content items, from different tables, can be in multiple packages. So I have the following schema:
Package table
=============
id
name
....
PackageContent table
=============
id
packageId
contentType
contentId
Guide table
=============
id
name
...
Form table
=============
id
name
How can I define the 'content' association for my packages in my Package.js model file in sails.js? I have not been able to find any information on combination foreign keys in sails.js or Waterline. I would hope to find something along the lines of:
// models/Package.js
module.exports = {
attributes = {
name: 'text',
....
content: {
through: 'PackageContent',
collection: contentType,
via: 'contentId'
}
}
};
I have a similar problem recently. My solution is to create different foreign keys in the intermediary model and set attributes as 'required:false'. In your example, the PackageContent table could look like this:
//PackageContent.js
module.exports={
attributes={
package:{
model:'package',
},
guide:{
model:'guide'
require:false
},
form:{
model:'form'
require:false
}
//other attributes...
}
}
In order to avoid duplicated package+content combination, you may need to write a beforeValidate method to check the duplication. I am not sure if this is a 'good design', but it solves the probelm

Accessing table with only foreign Keys in Entity Data Framework

i am new to Entity Data framework
i have tables
Actions ( Actionid (pk) ,Actionname , ... )
Roles ( Roleid(pk) , Rolename , .... )
ActionRoles( Actionid(pk,fk) , Roleid(fk) ) [Mapping Table]
Please Suggest me the LINQ to get the RoleNames for Perticular ActionID
(Note : there is No class created with Name ActionRoles in entitydesigner.cs as because it doesn't have any other column name then ActionId and RoleID )
Thank you in Advance
When you have a link table like this, adding all tables to the Entity Model should create 2 way relationship Properties between the 2 end tables, hiding the link table completely allowing you to access via something like:
IEnumerable<string> roleNames = Entities.Actions
.First(a => a.Actionid == actionid)
.Roles
.Select(r => r.Rolename);
where actionid is an int variable containing the actionid you're interested in.
For a discussion of how to handle many-to-many relationships such as this (both foreign keys must be in the ActionRoles primary key as indicated in the comment to your question), see these tutorials:
For EF 4.0: http://www.asp.net/entity-framework/tutorials/the-entity-framework-and-aspnet-–-getting-started-part-5
For Ef 4.1: http://www.asp.net/entity-framework/tutorials/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc-application