table_method how to check left outer join field is null - symfony1

I have a generated admin module with the following table method
class Exam{
...
function retrieveExamList(Doctrine_Query $q){
$rootAlias = $q->getRootAlias();
return $q->
innerJoin("$rootAlias.Person p")->
innerJoin("$rootAlias.Code ec")->
leftJoin ("$rootAlias.Order order")
->leftJoin("order.LatestVmOrderDetail od")->
addSelect("$rootAlias.*, p.*, ec.*, ec.name as exam_code_name, "
."order.order_id, od.payment_status, od.payment_date, od.payment_method_id"
);
}
In the admin list view, $exam->getPaymentStatus() returns null if the outer join returns null, what is the recommended way to check for this?
$exam->_data['payment_status'] === null
or is there something better?
I have tried
if ($exam->getPaymentStatus()){
which returns a Fatal error.
My problem is specifically that I have an order number in exam, but that order number doesn't exist in the corresponding relation

I usually use a simple if like this:
if ($exam->getPaymentStatus()) { /* ... */ }

Related

Doctrine Assocation Mapping Join Not Executing In ZF3

I am creating my first Association Mapping for a Join. This is also the first time I've used a Foreign Key in pgSQL.
I am working with ZF3. The error I am receiving is:
An exception occurred while executing 'SELECT p0_.reference AS reference_0, p0_.meta_keyword_reference AS meta_keyword_reference_1, p0_.add_date AS add_date_2, p0_.add_membership_reference AS add_membership_reference_3, p0_.remove_date AS remove_date_4, p0_.remove_membership_reference AS remove_membership_reference_5 FROM page_about_meta_keyword_link p0_ INNER JOIN meta_keyword m1_':
SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at end of input LINE 1: ...page_about_meta_keyword_link p0_ INNER JOIN meta_keyword m1_
The query I am trying to create is
SELECT MetaKeywords.Keyword FROM PageAboutMetaKeywordLink INNER JOIN MetaKeywords ON PageAboutMetaKeywordLink.MetaKeywordReference = MetaKeywords.Reference WHERE PageAboutMetaKeywordLink.RemoveDate IS NULL ORDER BY MetaKeywords.Keyword ASC
From my database experience I expect it is creating the error due to the missing
ON p0_.meta_keyword_reference = m1_reference
I don't understand how to communicate the Join. Based on the documentation I had expected this was automatic. Maybe I misunderstood.
The tables I am trying to Join are page_about_meta_keyword_link.meta_keyword_reference ON meta_keyword.reference . This is the first time I've created a foreign key in pgSQL.
This is the table structure for page_about_meta_keyword_link
CREATE TABLE public.page_about_meta_keyword_link
(
reference bigint NOT NULL DEFAULT nextval('page_about_meta_keyword_link_reference_seq'::regclass),
meta_keyword_reference bigint,
add_date timestamp with time zone DEFAULT now(), -- UTC
add_membership_reference bigint,
remove_date timestamp with time zone, -- UTC
remove_membership_reference bigint,
CONSTRAINT page_about_meta_keyword_link_pkey PRIMARY KEY (reference),
CONSTRAINT page_about_meta_keyword_link_fk FOREIGN KEY (meta_keyword_reference)
REFERENCES public.meta_keyword (reference) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT page_about_meta_keyword_link_reference_unique UNIQUE (reference)
)
This is the meta_keyword
CREATE TABLE public.meta_keyword
(
reference bigint NOT NULL DEFAULT nextval('meta_keyword_reference_seq'::regclass),
keyword text,
effective_date timestamp with time zone DEFAULT now(), -- UTC
membership_reference bigint,
CONSTRAINT meta_keyword_pkey PRIMARY KEY (reference),
CONSTRAINT meta_keyword_reference_unique UNIQUE (reference)
)
This is the query I've created in the Service; The complete Service is found here.
$repository = $this->entityManager->getRepository(PageAboutMetaKeywordLink::class);
$keywords = $this->entityManager->getRepository(MetaKeyword::class);
$qb = $repository->createQueryBuilder('l');
$qb ->join('\Application\Entity\MetaKeyword' , 'k')
->expr()->isNull('l.removeDate');
return $qb->getQuery()->getResult();
The Association Mapping I created is for meta_keyword_reference; The complete Entity is found here.
/**
* #var int|null
*
* #ORM\ManyToOne(targetEntity="MetaKeyword")
* #ORM\JoinColumn(name="meta_keyword_reference", referencedColumnName="reference")
* #ORM\Column(name="meta_keyword_reference", type="bigint", nullable=true)
*/
private $metaKeywordReference;
I have not made any changes to the MetaKeywords Entity. It is found here.
Overall the various sections of the web site will share the meta_keywords. If I understand correctly the connection I am trying to make is ManyToOne.
I am wanting to leave a good reference for other newbies as they are their journey with Zend Framework 3 - Doctrine. Please advise of edits I should be making to this post so it is clear, understandable and concise so I receive the help I need and others will benefit from this in the future.
You double declared a column (meta_keyword_reference). Looking at the docs (same page you linked in question), you've made a mistake in your Annotation. Remove the ORM\Column line (the definition is already in JoinColumn). If you need it to be nullable (not required), add nullable=true to the JoinColumn; use either, not both
/**
* #var int|null
*
* #ORM\ManyToOne(targetEntity="MetaKeyword")
* #ORM\JoinColumn(name="meta_keyword_id", referencedColumnName="id", nullable=true)
*/
private $metaKeywordReference;
Do not worry about declaring a "type", Doctrine will automatically match it to the column you're referencing. Also, you should be referencing Primary Keys. I've assumed reference is not the PK, so I've changed it to id, change it to what it actually is.
Next, I think you're also using DBAL QueryBuilder instead of the ORM QueryBuilder.
The Query you need would be like this:
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
/** #var QueryBuilder $qb */
$qb = $this->entityManager->createQueryBuilder();
$qb->select('l')
->from(PageAboutMetaKeywordLink::class, 'l')
->join(MetaKeyword::class, 'k', Join::ON, 'l.reference = k.id') // check these property names (NOT DB COLUMNS!)
->where('l.removeDate is null');
Might be a few small errors in there, but that should be about it.

Conditional Dynamic finder in grails

so i require something like the dynamic finder should change as per the condition, let me explain by code what i mean
the below code find all employee by status and lastdateattended
def employee = Employee.findAllByStatusAndLastDateAttended("INA",dateObject)
now i have two fields in Employee LastDateAttended and LastDateSigned , and now i want that if a record does not have LastDateAttended, then it should find by LastDateSigned , otherwise LastDateAttended, so another condition is like
def employee = Employee.findAllByStatusAndLastDateAttended("INA",dateObject)
and i somehow wants to join and use both the query based on a condition , can it be achieved ?, if possible please help
I think criteria query make sense here, something like following
Employee.createCriteria().list{
and{
eq('status','INA')
or {
eq('lastDateAttended',dateObject)
eq('lastDateSigned',dateObject)
}
}
}
Employee.list().findAll { emp->
emp.status == "INA" && ( emp.lastDateAttended!=null?
emp.lastDateAttended.equals(dateObject):emp.lastDateSigned.equals(dateObject)
)
}
Employee.findAllByStatusOrLastDateAttendedOrStatusAndLastDateAttended("INA",dateObject)

createAlias - how to specify LEFT join's ON condition in GORM?

I am facing a situation where the default LEFT join is joining on the undesired condition by default and I want to explicitly define it, how can I do it?
e.g. createAlias('fault.tgmap', 'tg', CriteriaSpecification.LEFT_JOIN) picks up the the wrong column i.e. group_id instead of fault_id by default (note that tg has two columns fault_id and group_id) in the resulting sql query.
How can I explicitly specify that ON which condition the join should happen i.e. fault.id = tgmap.fault_id instead of fault.id = tgmap.group_id?
To give domain specific information, trace, fault and tracegroup16 are the domain tables.
def results = trace.list(max:max, offset:offset) {
createAlias('fault', 'fault',CriteriaSpecification.LEFT_JOIN)
createAlias('fault.tgmap', 'tg', CriteriaSpecification.LEFT_JOIN)
createAlias('tg.traceGroup16','tr', CriteriaSpecification.LEFT_JOIN)
projections
{
property('fault.id')
property('tr.geckId')
}
}
It would be useful to see what your model looks like, but I think you can do something like this:
Fault.createCriteria().list() {
createAlias('tgmap', 'tg', CriteriaSpecification.LEFT_JOIN)
eqProperty('tg.fault.id', 'id')
}
Reference

Laravel 4 Eloquent Query Builder - Complicated joins with variable

I'm trying to replicate a join like so using the laravel query builder:
LEFT JOIN content_userdata
ON content_id = content.id
AND user_id = $user_id
I have discovered that I can do additional "ons" using the following function in my model which extends Eloquent
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')->on('content_userdata_user_id', '=', 10);
});
}
But this creates two problems. Firstly I cannot get the $user_id variable into the function and secondly even if I hardcode it for testing purposes as I have done above (to int "10") Laravel encases it in ` meaning that it is interpreted as a column name when it shouldn't be, like so:
left join `content_userdata`
on `content_id` = `content`.`id`
and `user_id` = `10`
So now I have two problems.
I cannot get the $user_id into the join function when using query scopes
Even if I could I cannot send a variable to the join since it always interprets it as a column name
Why would I want to do this?
I realise one response may be to place it in a where. However I am trying to do it this way as the join may not necessarily return any results (hence the left join), since the content_userdata table contains things like a users rating for a piece of content. If I use a where then results with nothing in the content_userdata table will not be returned, where as if I can put it in the join then they will be returned due to the left join.
Is there anyway to achieve this in Laravel and if not what are the alternatives, obviously completely changing ditching Laravel is over the top but the only alternative I can think of is to get the userdata in a separate query.
You need to pass the variable to the closure using the use keyword - which imports the variable into scope. Example:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join) use ($user_id)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')
->on('content_userdata_user_id', '=', DB::raw($user_id));
});
}
This is a PHP syntax related issue and not a Laravel limitation!
In the accepted answer, just adding quotes around the DB::raw part of the query will not fully protect it from sql injection. Just pass some quotes in your user_id and see. To parameterize you can do something like this:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')
->on('content_userdata_user_id', '=', DB::raw('?'));
}
->setBindings(array_merge($query->getBindings(),array($user_id)));
}
Notice in this example that you don't have to pass the variable into the closure. Alternatively you could try and write this part completely raw.
UPDATE: Taylor added joinWhere, leftJoinWhere... if you have a function join just use ->where and ->orWhere from within the Closure.
I managed to fix this myself, there's a note at the bottom of why it's not completely optimal but here's how to do it anyway:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin('content_userdata', function($join) use ($user_id)
{
$join->on('content_userdata_content_id', '=', 'content.content_id')->on('content_userdata_user_id', '=', DB::raw('"'.$user_id.'"'));
});
}
Note the use of "use ($user_id)" as suggested by #Half Crazed.
DB::raw() is used to wrap $user_id in quotes even though it's an integer and not a string. This will stop Laravel automatically using ` which makes it MySQL interpret it as a column name.
Performance: One thing to note is that MySQL queries can be considerably faster when using an integer rather than a string and will interpret it as a string if it's wrapped in quotes. I'm not worrying about that for now, but I figured I should mention it if others are using this as a solution.
Why dont you just use relationships? That is the whole point of an ORM like Eloquent?
Something like this;
class User extends Eloquent {
public function userdata()
{
return $this->hasOne('Userdata');
}
}
$result= User::find(1)->userdata();
edit to show you can do whatever you want with relationships
Option 1:
$place = new Place;
$array = $place->with(array('users' => function($query)
{
$query->where('user_id', $user_id);
}))->get();
var_dump($array->toArray());
or Option 2:
$place = new Place;
$array = $place->with('users')->where('user_id', $user_id)->get();
var_dump($array->toArray());
Both give different results - but you get the idea
Your first problem: You should use PHP syntax for closure as the answer of Half.
About your second problem, I think the part AND user_id = $user_id of the query does not belong to a JOIN clause but a WHERE clause because it just depends on one table, not both in this joining relationship. I think you should use a subquery like this:
public function scopeJoinUserData($query, $user_id)
{
return $query->leftJoin(\DB:raw("(SELECT * FROM content_userdata WHERE user_id = {$user_id}) AS t"), function($join)
{
$join->on('t.content_id', '=', 'content.content_id');
});
}
However, as you see, let be sure that the $user_id variable is safe because we use \DB:raw method.

Copy an object with relations in which there is a Sortable object throws an Duplicate Entry error

In an old projet using symfony 1.4 There are an object Product linked to other objects such as Translation ProductPlatform ...
What I want to do is copy the Product object with all its relations.
Pretty simple with $product->copy(true) BUT
It doesn't copy the relations, so i need to do :
$this->loadReference('Translation');
$this->loadReference('ProductPlatforms');
foreach ($this->ProductPlatforms as $platform) {
$platform->loadReference('Translation');
}
$newProduct = $this->copy(true);
$newProduct->save();
return $newProduct;
The throwed error is this :
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1-131' for key 'product_platform_position_sortable_idx'
What I don't understand is the following requests :
SELECT p.id AS p__id, p.position AS p__position FROM product_platform p
WHERE (p.product_id = 'EndNote (copie)') ORDER BY p.position desc LIMIT 1;
----
INSERT INTO product_platform (publish_configuration, product_id, platform_id, position)
VALUES ('1', '131', '1', '1')
And then, it reinsert, for another object, the same position :
INSERT INTO product_platform (publish_configuration, product_id, platform_id, position)
VALUES ('1', '131', '3', '1');
Why there is, instead of an ID, the Name of my object in the Where clause WHERE (p.product_id = 'EndNote (copie)'). I think this is the reason it tries to insert the same value.
Ok, it's a bug with the Sortable behaviour, we sent a PR :
https://github.com/bshaffer/csDoctrineActAsSortablePlugin/pull/25

Resources