How do I use join and indexBy in Yii2? - join

I have this, but it loads each and every ebay row individually, generating thousands of SQL statements:
$products = \app\models\Product::find()
->joinWith('ebay', false, 'inner join')
->indexBy(function($row){return $row->ebay->epid;})
->all();
I tried this, but it gave an error: 'Getting unknown property: app\models\Product::ebay.epid'
$products = \app\models\Product::find()
->joinWith('ebay', false, 'inner join')
->indexBy('ebay.epid')
->all();
Setting eager loading = true doesn't help either. It still loads each row individually then loads them again at the end.
How can I efficiently join a table in Yii and index by a value in the joined table?

You won't be able to do it with indexBy. However, ArrayHelper::index can index an array on a related model field. So here's how it can be done:
$products = \app\models\Product::find()
->with('ebay')
->all();
ArrayHelper::index($products, 'ebay.epid');
The code will run two queries, one to get all products, one to get all related ebay products. Then the array will be indexed with no DB queries at all.

I ended up doing it manually for a subset of the ids and it only uses 2 queries. I'd still be interested in the indexBy though.
$products = Product::find()->joinWith('ebay', true, 'inner join')->where(['ebay.epid' => $productIds])->all();
$ebayProducts = array();
foreach ($products as $p) {
$ebayProducts[$p->ebay->epid] = $p;
}

If you want index by relation recods via joinWith() or with() results you can use following:
->with(['relationName' => function($q) {
$q->indexBy('field_name');
}])

Related

Yii2: How to do a inner join of three tables?

I have a many-to-many relationship so I have three tables (administrators, offices and the intermediate table):
I need to do a very simple innerjoin of the three tables to show lastnames and their offices with Active Record. I tried but I couldn't.
This is what I tried:
$admins = Admins::find()
->joinWith(Oficinas::tableName())
->all();
echo '<pre>';
print_r($admins);
echo '</pre>';
die();
Also I would like to know how to show the SQL query so it can help me to find a solution.
You need to specify the relation name for joinWith() rather than table names. Since there isn't any info on the relation names I will use simple innerJoin using table names as per your requirements to display the last name and the office name for the admins.
Admins::find()
->alias('a')
->select('a.lastnameadm, o.nombreofi')
->innerJoin('admin_oficinas ao','ao.idadm = a.idadm')
->innerJoin('oficinas o','o.idofi = ao.idofi')
->all();
Try this:
TABLE_NAME_1::find()
->join('inner join',
'table_name_2',
'table_name_2.column = table_name_1.column'
);
->join('inner join',
'table_name_3',
'table_name_3.column = table_name_2.column'
)->all();
but if you can also use Via like the following example:
public function getClassschedule()
{
return $this->hasOne(TABLE_NAME_2::className(), ['table_name_1.column' => 'table_name_2.column'])
->via('tableName3');
}
when excuded, the results should be obtained like the previous example. so there's no need to repeated relations. full documentation can be seen here :
https://www.yiiframework.com/doc/guide/2.0/en/db-active-record

Getting activerecord readonly error when adding a where clause

I have two queries for two different pages:
all_teams = Team.includes(:terms, :labs, people: [:schools], term_enrollments: [:lab])
current_teams = Team.includes(:terms, :labs, people: [:schools], term_enrollments: [:lab]).where(terms: {id: 3)
I am trying to update the term_enrollments with each query. For the first query, everything is good. I do this with the first query:
a = all_teams.first.term_enrollments.first
a.update_attributes term: Term.find(2)
=> true
When I try to do this with the second query, it doesn't work. Here's the code I try to do for the second query:
a = current_teams.first.term_enrollments.first
a.update_attributes term: Term.find(2)
But instead of working it says:
ActiveRecord::ReadOnlyRecord: TermEnrollment is marked as readonly
Both of these queries are in controller actions and feed a page that loops through the activerecord relation and shows an edit form for each instance.
current_teams is probably doing some join which in some cases causes the records to become readonly. You can get around this with
a = all_teams.first.term_enrollments.first.readonly(false)
a.update_attributes(term: Term.find(2))

DynamoDB contains value in an array using rails

I'm studying DynamoDB using rails and I got a doubt.
I not be able to find a solution on web, so If you can solve it I'll thank.
The doubt is how can I find values into array saved on a table, for example:
I have a lot of data in my_table where there are fields called "numbers" that are arrays like:
[1,2,3,4]
[3,4,5,6]
[1,3,4,7]
[4,7,8,10]
[8,9,12,14]
[12,14,16,20]
So, I want select all entries that contains numbers 1,3,4. In this case four results.
So, my code is
result = dynamodb.scan({
table_name: "my_table",
select: "ALL_ATTRIBUTES",
attributes_to_get: ["numbers"],
scan_filter: {
"numbers" => {
attribute_value_list: [1,3,4],
comparison_operator: "CONTAINS"
}
}
})
But I get this error: One or more parameter values were invalid: Invalid number of argument(s) for the CONTAINS ComparisonOperator
How can I do this action using dynamo DB?
Thanks a lot
Try this and let me know if it works, I know from experience that DynamoDB is very painful to filter.
result = dynamodb.scan(
table_name: 'my_table',
expression_attribute_values: {
':one' => 1,
':two' => 2,
':three' => 3,
':four' => 4
},
filter_expression: 'contains(numbers, :one) OR contains(numbers, :two) OR contains(numbers, :three) OR contains(numbers, :four)'
)
I can't think of anything simpler currently, the method you linked is marked as deprecated, instead you should use expression_attribute_values and filter_expression.

LINQ to SQL - Filtering the dataset between two nested collections

I have an MVC 3 project in Visual Studio c#. I have a LINQ to SQL query which works fine and by following an example listed elsewhere on stackoverflow:
Comparing two lists using linq to sql
I have been able to successfully reduce my results where my two nested collections match. This is the bit of code that did the trick (example from the link above):
var anyDesiredSkills = canidateSkills.Any( c => desiredSkills.Select( ds => ds.SkillId ).Contains( c.SkillId ) );
I've adapted this successfully, but now I need to be able to filter records using more than one condition. I was wondering if anyone would be able to adapt the above to show how you could include more than one condition?
To give you some background on what my goal is:
A search page where you can select any number of contacts
Each contact added to the search criteria may/may not have a 'role' assigned. If a role is present this should be factored in to the query.
Results returned based on this dynamic criteria.
Thanks in advance for any and all help :O)
It sounds like you're looking for something like:
var desiredSkillIds = desiredSkills.Select(_=>_.SkillId).ToList();
var matchingContacts =
from contact in Contacts
where contact.Role == null || desiredRoles.Contains(contact.Role)
where contact.Skills.Any(cs=> desiredSkillIds.Contains(cs.SkillId))
select contact;
Or in method-based syntax:
var matchingContacts = Contacts
.Where(contact => contact.Role == null || desiredRoles.Contains(contactRole))
.Where(contact => contact.Skills.Any(cs => desiredSkillIds.Contains(cs.SkillId)));
Here's the final code I used:
servicelist = servicelist.Where(
d => d.ContactSelection.Any(
h => model.ContactFilter.Select(ds => ds.StaffNumber).Contains(h.StaffNumber)
&&
model.ContactFilter.Select(ds => ds.ContactRole).Contains(h.ContactRole) || model.ContactFilter.Select(ds => ds.StaffNumber).Contains(h.StaffNumber) && model.ContactFilter.Select(ds => ds.ContactRole).Contains("0"))
);
Note that the last filter .Contains("0) is the value of '-- select role --' which is an option injected in to the drop down. Hope this helps anyone else!

symfony pagination with join

How do you paginate a query that has a join in symfony? I am trying this code but it times out:
$query=Doctrine_Query::create()
->select('c.name, d.phone')
->from('company c, companyDetails d')
->where('c.companyId=d.companyId');
$pager = new sfDoctrinePager('company',10);
$pager->setQuery($query);
$pager->setPage(1);
$pager->init();
$this->companys=$pager->getResults();
Your paginator code seems fine. I thk the problem is with your query.
Try running it without the pager and see the result.
If you continue facing issues with the query,Give this a try.
$query=Doctrine_Query::create()
->select('c.name, d.phone')
->from('company c')
->innerJoin('c.companyDetails d');
I havent tried it myself but it should work if your schema relations are defined the way i think.
One more thing, probably u already know that.
$pager->setPage(1);
should be something like
$pager->setPage($request->getParameter('page', 1));
I needed to change the count query in the pagination. To do this:
$pager = new Doctrine_Pager($query,$request->getParameter('page',1),10);
$pager->setCountQuery('SELECT COUNT(id) FROM items WHERE city_id='.$city.' AND category_id='.$category);
$this->pager=$pager;
The only thing you have to do is in your pagination helper, use:
$pagerRange = $pager->getRange('Sliding',array('chunk' => 5));
$pages = $pagerRange->rangeAroundPage();

Resources