Doctrine pain while accessing members of a left join that are null - symfony1

I have a Doctrine query in my model say Model_1Table.class.php that left joins with Model_2. Model_2 relates to Model_1 on a many-to-one relation. (it really is a one-to-one relation and not a many-to-one but the schema got designed so)
My query is:
$this->select('m1.*, m2.primaryKeyOfModel2')->leftJoin('m1.model2 m2')->where('m1.record = ?', some_value);
Inspecting SF Debugger logs, it retrieves the information as it should, retaining all table records from Model_1 that satisfy the where clause and retaining all values of Model_2 along with null values for those that do not exist.
The pain begins when I want to access this information, and conditionally render information dependent upon the presence/absence of model_2 records in my table. which I do like this:
<?php foreach($Model1Records as $Model1Record) :
if($Model1Record->getModel2()) :
foreach($Model1Record->getModel2() as $Model2Record):
//do something
end foreach ;
else: //basically checking for if(!($Model1Record->getModel2()))
// do something else
endif;
endforeach ; ?>
Now based on this it should really work, but I think what is happening is that for the null values of model2 it fails. how do I infer that, well because if I remove the if-else statements in the above record and simply access the collection of Model2Record with a foreach loop, the available rows in Model2Record are retrieved, but then offcourse it does not access the null values. Essentially, the information I get from this is what I would typically get from an inner join.
To frame a question, how do I access records of the Model1 that have Model2 records as null values ?

Not sure what the default return is for a null relation between model1 and model2 via doctrine, but obviously it's not returning a false, otherwise you would be hitting the else condition, try 'instanceof' instead, for example:
<?php foreach($Model1Records as $Model1Record) :
if($Model1Record->getModel2() instanceof Model2) :
You could also try casting the Model1 object to an array:
<?php foreach($Model1Records as $Model1Record) :
<?php $model1 = $Model1Record->toArray(); ?>
if(isset($model1['Model2'])) :
-- Update --
If it's returning a Doctrine_Collection instance, you should be able to do:
<?php foreach($Model1Records as $Model1Record) :
if($Model1Record->getModel2()->count() > 0) :

Related

Rails: where condition with "OR is NULL" inside

I want to get all the records where a datetime attribute is either NULL or within a timeframe. How can I add a or where disbursed_at IS NULL to the condition inside the where?
My actual query is way longer, I wouldn't want to repeat it all using .or
Investor.left_join(:disbursements).where(disbursements: { disbursed_at: year_period })
Generally speaking you can add an OR column_X IS NULL condition by providing an Array, containing at least one nil element, as part of the Hash arguments.
For Example:
Investor
.left_join(:disbursements)
.where(disbursements: { disbursed_at: [year_period,nil] })
Assuming year_period is a Range this should result in
SELECT
investors.*
FROM
investors
LEFT OUTER JOIN disbursements ON disbursements.investor_id = investors.id
WHERE
disbursements.disbursed_at BETWEEN xxxxx AND xxxxx
OR disbursements.disbursed_at IS NULL

stored procedure returning 2 result set mybatis 3

I am facing a problem in mybatis 3. I have a stored procedure that returns two result sets(resultset of object1, result set of object2).
I have created a resultMap for each result set
result map for object1(Create a class pojo Object1)
result map for object2(Create a class pojo Object2)
which give as something like this:
<resultMap id="object1" type="Object1" />
<resultMap id="object2" type="Object2" />
and for the call of the stored proc I have this:
<select id="pscall" parameterType="Integer"
resultMap="object1,object2" statementType="CALLABLE">
{ CALL PS(
#{id, mode=IN},
) }
</select>
Now when executing all this, I got effectively two lists with the number wanted of object1 and object2, but these two lists are filled with null objects, like the first list I got 3 elements and they are all null and the second 20 elements all null.
I know it's something wrong I made with the mapping, but at this point, I cannot see where the problem is.
You're asking us to deduce quite a lot, but I think it is clear that the basic mechanism of multiple result set handling is working correctly (you are getting the correct number of objects). What's not working is mapping rows to objects. That can only be happening if Object1 and Object2 do not have any attributes that match the columns coming back from the SP.
For example, if the first result set contains "ID, DESCRIPTION", then Object1 must have "id" and "description" properties (it is case insensitive).
If you don't know what will be coming back from the SP, then change the type of each <resultMap> to map. Then MyBatis will build lists of maps and you can look in the map to see the values that came back from the SP and their column names.

Activerecord specifications with 2 different models

I need to find a way to display all Vacancies from my Vacancy model except the ones that a user already applied for.
I keep the IDs of the vacancies a certain user applied for in a seperate model AppliedVacancies.
I was thinking something line the lines of:
#applied = AppliedVacancies.where(employee_id: current_employee)
#appliedvacancies_id = []
#applied.each do |appliedvacancy|
#appliedvacancies_id << appliedvacancy.id
end
#notyetappliedvacancies = Vacancy.where("id != ?", #appliedvacancy_id)
But it does not seem to like getting an array of IDs. How would I go about fixing this?
I get following error:
PG::DatatypeMismatch: ERROR: argument of WHERE must be type boolean, not type record
LINE 1: SELECT "vacancies".* FROM "vacancies" WHERE (id != 13,14)
^
: SELECT "vacancies".* FROM "vacancies" WHERE (id != 13,14)
This is purely an SQL problem.
You cannot use != to compare a value to a set of values. You need to use the IN operator.
#notyetappliedvacancies = Vacancy.where("id NOT IN (?)", #appliedvacancy_id)
As an aside, you can drastically improve the code you've written so far. You are needlessly instantiating complete ActiveRecord models for every record found in your applied_vacancies table, when all you need are the IDs.
A first pass at improvement would be to use pluck to skip the entire process and go straight to the list of IDs:
ids = AppliedVacancies.where(employee_id: current_employee).pluck(:id)
#notyetappliedvacancies = Vacancy.where("id NOT IN (?)", ids)
Next, you can go a step further and eliminate the first query all together (or rather, combine it with the last query as a sub-query) by leaving it as an AREL projection which can be subbed into the second query directly:
ids = AppliedVacancies.select(:id).where(employee_id: current_employee)
#notyetappliedvacancies = Vacancy.where("id NOT IN (?)",App)
This will generate a single query:
select * from vacancies where id not in (select id from applied_vacancies where employee_id = <value>)
Answer like #meagar, but Rails 4 way:
#notyetappliedvacancies = Vacancy.where.not(id: #appliedvacancy_id)

Rails: how to correctly modify and save values of records in join table

I would like to understand why in Rails 4 (4.2.0) I see the following behaviour when manipulating data in a join table:
student.student_courses
returns all associated records of courses for a given user;
but the following will save changes
student.student_courses[0].status = "attending"
student.student_courses[0].save
while this will not
student.student_courses.find(1).status = "attending"
student.student_courses.find(1).save
Why is that, why are those two working differently, is the first one the correct way to do it ?
student.student_courses[0] and student.student_courses.find(1) are subtly different things.
When you say student.student_courses, you're just building a query in an ActiveRecord::Relation. Once you do something to that query that requires a trip to the database, the data is retrieved. In your case, that something is calling [] or find. When you call []:
student.student_courses[0]
your student will execute the underlying query and stash all the student_courses somewhere. You can see this by looking at:
> student.student_courses[0].object_id
# and again...
> student.student_courses[0].object_id
# same number is printed twice
But if you call find, only one object is retrieved and a new one is retrieved each time:
> student.student_courses.find(1).object_id
# and again...
> student.student_courses.find(1).object_id
# two different numbers are seen
That means that this:
student.student_courses[0].status = "attending"
student.student_courses[0].save
is the same as saying:
c = student.student_courses[0]
c.status = "attending"
c.save
whereas this:
student.student_courses.find(1).status = "attending"
student.student_courses.find(1).save
is like this:
c1 = student.student_courses.find(1)
c1.status = "attending"
c2 = student.student_courses.find(1)
c2.save
When you use the find version, you're calling status= and save on entirely different objects and since nothing was actually changed in the one that you save, the save doesn't do anything useful.
student_courses is an ActiveRecord::Relation, basically a key => value store. The find method would only work on a model

Enforcing uniqueness on a relation based on a single column

I have a class method on my Consumer model that functions as a scope (acts on an ActiveRecord::Relation, returns an ActiveRecord::Relation, can be daisy-chained) and returns doubles of some consumers. I want it to only return unique consumers, and I can't seem to find either a way to do it with Rails helpers or the right SQL to pass in to get what I want. I feel like there's a simple solution here - it's not a complicated problem.
I essentially want Array#uniq, but for ActiveRecord::Relation. I tried a select DISTINCT statement
Consumer.joins(...).where(...).select('DISTINCT consumers.id')
It returned the correct 'uniqueness on consumers.id' property, but it only returned the consumer ids, so that when I eventually loaded the relation, the Consumers didn't have their other attributes.
Without select DISTINCT:
my_consumer = Consumer.joins(...).where(...).first
my_consumer.status # => "active"
With select DISTINCT:
my_consumer = Consumer.joins(...).where(...).select('DISTINCT consumers.id').first
my_consumer.status # => ActiveModel::MissingAttributeError: missing attribute: status
I didn't think that an ActiveRecord::Relation would ever load less than the whole model, but I guess it will with the select statement. When I query the Relation after the select statement for the class it contains, it returns Consumer, which seems strange.
.select('DISTINCT consumers.*')

Resources