How to fetch out complex result from the database - Yii - yii

i am facing problem to fetch out records from table using Yii relationship,
i have 3 tables
1) Students -> ID, Name, Roll_Number
2) Subjects -> ID, Name
3) Students_taken_Subjects -> ID, Student_ID, Subject_ID
Suppose there are number of students have taken more than one subjects which are stored in the 3rd table "Students_taken_Subjects" then how i can fetch out the list of students taken any specific subject?
e.g. list of students taken maths
which one from below relationships are correct and how can i get results into the $dataProvider variable?
'Students'=>array(self::HAS_MANY, 'Subjects', 'Student_ID'),
and
return array(
'Students'=>array(self::MANY_MANY, 'Subjects',
'Students_taken_Subjects(student_id, subject_id)'),
);

The relationship between Subjects and Students is MANY_MANY, but you've written it a bit wrong, This is what you need:
class Subjects extends CActiveRecord
{
// ...
public function relations()
{
return array(
'Students'=>array(self::MANY_MANY, 'Students', 'Students_taken_Subjects(Subject_ID, Student_ID)'),
);
}
// ...
}
Once you've written this relation, the Subjects active record will have a Students property that returns an array with the 0 or more students taking that subject. You can access them like this:
$subject = Subjects::model()->findByPk($pk);
$students = $subject->Students; // an array with the subject's students
The above code will result in two DB accesses, one for the $subject and one for the related $students. This might be fine, but if you are accessing a lot of subjects it could become a problem with too much "lazy loading". You can tell Yii to "eager load" the students along with the subjects like this:
$subjects = Subjects::model()->with('Students')->findAll();
Here you are finding all of the subjects, but alerting Yii--using with('Students')--that you'll be wanting each subject's student information as well. This ensures that all of the students related to the subjects you find will be grabbed at once. An alternative to the with() function is to use a criteria's with property:
$criteria=new CDbCriteria;
$criteria->with = array('Students');
$subjects = Subjects::model()->findAll($criteria);
Either way, when you do ask for a subject's students like this:
$subjects[0]->Students;
$subjects[1]->Students;
// ...
you will not get another DB call each time because Yii already loaded the data.
You'll need to provide more details about what you are wanting to do with the students in the data provider before I can give any more details about that.

Related

Entity Framework Many-to-Many Query

I'm struggling writing a query in Entity Framework that deals with a many to many relationship that I have set up. What I want to do is get the items from TableA that belong to a relationship with TableB and at the same time know from the results which relationship was a correct match.
For instance, if I'm using Students and Courses, I want to look for all the students that are in a set of courses and also return only those courses that matched. I very specifically want to start the query with Students, as this can easily be accomplished by just looking at the Courses navigation property to get the list of students.
What I want is a list of Students where each Student contains only the set of Courses in my query (NOT every course the student is taking).
Something like the below is close, I get the correct list of Students, but the navigation property for Courses shows all Courses, not the subset from my query. I want to avoid having to query again if possible, and just return the set of Students / Courses I need.
Dim listOfStudents = From s In Students
From c In s.Courses
Where listOfCourseIds.Contains(c.CourseId)
If there's no junction table between the two, then try:
from s in dc.Students
from c in s.Courses
where c.CourseID == courseID
select s;
If entity has a junction table between the two, try:
from s in dc.Students
from e in s.StudentsCourses
where e.Course.CourseID == courseID
select s;

Filtering out records in SQL with a join

I am creating an app that makes guest lists for greek life events at universities.
The two tables I am working with are 'student' table and 'participant' table.
The fields in the student table are: student_id, student_name, university, and chapter.
Students with chapter id's are considered members, and students without chapter id's are considered guests when making guest lists(participant table).
The participant table fields are: participant_id, member(which is related to student_id), guest (which is also related to student_id), and event.
When trying to add guests to the guest list for an event, I wrote the following sql query to filter out students from different universities and that aren't in chapters and weren't already on the list:
$student = getColumn("SELECT guest FROM participant WHERE event = '$event'");
$university = getSqlValue("SELECT university FROM student WHERE student_id = '$member'");
$f->setOption('filter',
"SELECT student_name
FROM `student`
LEFT JOIN participant ON student.student_id = participant.guest
WHERE student.chapter = ''
AND student.university = '$university'
AND participant.guest != '$student'");
So, I know this isn't going to work, because I have a whole array for $student, but even if I try it with one student id, the query doesn't work. It returns empty. If I remove the last AND particpant.guest!= $student, then the query returns all the students at the university that are not members of a chapter.
My question has two parts:
Why wouldn't that query work with one value for student?
Can someone think of a better way to go about doing this?

Taking in Array as path param and hibernate query

I am trying to retrieve a list of students from a list inserted into the pathparam which retrieve the students based on their studentId using hibernate.
To retrieve a single student with a single studentId, I use the following code which is working.
student = (student) session.createQuery("from student as student
where student.studentId = :studentId")
.setString( "studentId", studentId ).uniqueResult();
However, when I try to pass in a list of studentIDs to return more than 1 student, I tried this code but it doesn't work. Hopefully someone can shed some light.
Any help is appreciated.
students = session.createQuery("from student as student
where student.studentId in :studentList").setParameterList( "studentList", studentList ).list();
Probably is due to missing parenthesis around :studentList; correct syntax for IN clause is IN (values list) so change your query as
students = session.createQuery("from student as student
where student.studentId in (:studentList)").setParameterList( "studentList", studentList ).list();
From Query.setParametersList() javadoc:
Bind multiple values to a named query parameter. The Hibernate type of
the parameter is first detected via the usage/position in the query
and if not sufficient secondly guessed from the class of the first
object in the collection. This is useful for binding a list of values
to an expression such as foo.bar in (:value_list).

SQL subquery result in LINQ and Entity Framework Code First

I want to make a query that'll return me entities and additionally a number of one of their associated entities. For example:
select *, (select COUNT(*) from Forms where Contact_Id = Contacts.Id)
as FormsCount from Contacts;
My Contact entity has a property named FormsCount, but it isn't mapped since there's no column named like that in the table. Is it possible to write one LINQ query that'll return me Contact entities with the additional FormsCount property filled in?
Alternatively, I'd be happy if I could get the FormsCount values in a separate field and I can copy them to the entities manually. The result from the query could be in this form for example:
{
Contact Contact;
int FormsCount;
}
Then I can iterate over the results and copy FormsCount to Contact. Maybe this can be achieved by using projections?
I know how to do that using 2 queries:
a) fetch contact entities first
b) fetch pairs or contact ID and FormsCount for contacts returned in the first query.
But I'd like to do that using one query. Also, I don't want the FormsCount property to be always filled in my Contact entity, I want to have a control over that. Any ideas?
Thanks,
Michal
You are right about the projection.
If Contact has a navigation property Forms you can project:
from c in context.Contacts
select new { Contact = c, FormsCount = c.Forms.Count() }
If not, you'll have to use a subquery:
from c in context.Contacts
select new
{
Contact = c,
FormsCount = context.Forms.Count(f => f.Contact_Id == c.Id)
}
EF will handle both situations in one SQL query.

How to build custom PLINQO query by multiple id's?

Here's my table structure
Places
PlaceId PK
Name
...
PlaceCategories
CatId PK
Name
...
PlaceCats
PlaceId PK
CatId PK
Here's my query that pulls Places based on category id (table join)
public static IQueryable<Places> ByPlaceCat(this Table<Places> table, Expression<Func<PlaceCats, bool>> predicate) {
var db = (DataContext)table.Context;
var innerBizBase = db.PlaceCats.Where(predicate);
return db.Places.Join(innerBizBase, a => a.PlaceId, ab => ab.PlaceId, (a, ab) => a);
}
I use it like this:
places = Db.Places.ByPlaceCat(a => a.CatId == 5);
But I want to be able to pull based on a List<int> of category id's. Looking through the generated PLINQO code, a query that pulls by multiple PlaceId's (but not using a joined table) looks like this:
public static IQueryable<Places> ByPlaceId(this IQueryable<Places> queryable, IEnumerable<long> values)
{
return queryable.Where(p => values.Contains(p.PlaceId));
}
How could I essentially merge those two queries, to let me pass in a List<int> of CatId's to query by? This LINQ/PLINQO query is melting my brain. Thanks in advance!
You would need to write a extension method like this:
public static IQueryable<Places> ByPlaceCats(this Table<Places> table, IEnumerable<int> catIds)
{
var db = (TestDataContext)table.Context;
var places = (from placeCat in db.PlaceCats
join place in db.Places on placeCat.PlaceId equals place.PlaceId
where catIds.Contains(placeCat.CatId)
select place);
return places;
}
Please note that the PlaceCats table could be made into a ManyToMany relationship by adding two foreign keys to the proper tables. Once this change has been made than PLINQO will automatically generate the correct code and will create a link between the two tables skipping the intermediary table. So you could get a collection of PlaceCategories associated to the current Places entity by accessing a property on the Places entity.
Please remember to contact us if you have any questions and be sure to check out the community forums located here and PLINQO forums here.
Thanks
-Blake Niemyjski (CodeSmith Support)