Zend Db joinLeft with parameterized value - zend-framework

I need to joinLeft within Zend_Db ala:
$select->joinLeft(array('ATAG' => 'ad_tags'),
array('ADM.id = ATAG.ad_id AND ADM.site_id = ATAG.site_id AND ATAG.tag_id = ?', $input_vars['tag']),
array('tag_id'))
->order('ATAG.tag_id DESC')
->limit('1');
However, I can't use an array as the second parameter, because it only tags a string; how can I pass in the value, without actually embedding it in the string?

Based on your example you need to use quoteInto (considering you're in Zend_Db_Table):
$select->joinLeft(array('ATAG' => 'ad_tags'),
'ADM.id = ATAG.ad_id AND ADM.site_id = ATAG.site_id AND ' .
$this->getAdapter()->quoteInto('ATAG.tag_id = ?',$input_vars['tag']),
array('tag_id'))
->order('ATAG.tag_id DESC')
->limit('1');
You can also do the same with:
$select->from(array('ADM' => 'adm_table'),'*')
->joinLeft(array('ATAG' => 'ad_tags'), 'ADM.id = ATAG.ad_id', 'ATAG.tag_id')
->where('ADM.site_id =ATAG.site_id')
->where('ATAG.tag_id = ?',$input_vars['tag'])
->limit(1);
If you're in Zend_Db_Table you need to set
$select->setIntegrityCheck(false);
(Note that adm_table is just an example, since you did not tell me the table name)

Related

Is it possible to combine multiple IQueryable into a single one without using Union?

Good evening. Is it possible to combine multiple IQueryable into a single one without using Union in EF Core 2? The main problem is that it doesn't allow to use methods like .Union(), .Except() etc.
It's important that IQueryable should be executed as a single query.
This is what I want to get: toCreateSubquery.Union(toDeleteSubquery)
The queries I want to combine are listed below.
var toCreateSubquery =
validLinks
.Where(
rl => !existingLinks.Any(
el => el.Contract == rl.Contract && el.Estate == rl.Estate)) // Except() doesn't work'
.Select(x => new {x.Contract, x.Estate, Type = ActionType.Create});
var toDeleteSubquery =
existingLinks
.Where(el => !validLinks.Any(rl => el.Contract == rl.Contract && el.Estate == rl.Estate))
.Select(x => new {x.Contract, x.Estate, Type = ActionType.Delete});
This is the visualization of the problem I'm solving.
I want to get the union of these sets without intersection and be able to distinguish belonging to one of these sets.
Just in case, I attach the code of getting these sets:
var validLinks = from ac in _context.AreaContracts
from e in _context.Estate
where (from al in e.AreaLinks select al.Area.Id).Contains(ac.Area.Id) ||
e.Geometry.Intersects(
ac.Area.Geometry)
select new { Contract = ac.Contract.Id, Estate = e.Id };
var existingLinks = from sd in _context.SquareDistributions
select new { Contract = sd.Contract.Id, Estate = sd.Estate.Id };
Thank you for your attention.

Adding Data to a very nested child object using EntityFramework

I am trying to add a note to my event object. I am getting an error using this code
Note noteToAdd = new Note { State = State.Added, NoteText = note };
Patient patient = context.Patients.Find(patientId);
patient.State = State.Modified;
patient.MobilePatient.State = State.Modified;
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Note = noteToAdd;
context.ApplyStateChanges();
Is there a better way to do it using Linq To Entity?
The error that I am having is :
{"Invalid column name 'Note_Id'."}
and the SQl that is being generated is a SELECT instead of INSERT.
Thank you
but your map shows a one-to-many relation between Note and Event...
all of your code remain as they are, but instead of this line :
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Note = noteToAdd;
replace these lines:
noteToAdd.EventID = oEvent.ID; // replace field names, to exactly what they are;
context.Note.Add(noteToAdd);
var oEvent = patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid);
oEvent.NoteID = noteToAdd.ID; // replace field names, to exactly what they are;
also i think if you don`t write these two:
var oEvent = patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid);
oEvent.NoteID = noteToAdd.ID; // replace field names, to exactly what they are;
there is not any problem, i`m not sure
According to your map, Event entity has a list of Note as navigation property, and i think you should add to this collection instead, what you write in this line:
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Note = noteToAdd;
i think should be like this:
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Add(noteToAdd);
in addition, what kind of error you get ? can you explain your error ?
are sure there is no add method on Event navigation property
why don`t you try to add note from context directly? like:
context.Note.Add(noteToAdd);

"Unable to create a constant value of type .. Only primitive types are supported ..'' in EF query?

I have a work around for this issue, however I would appreciate it if someone could explain why this is happening and how I would design this for large datasets where my work around would not be viable.
The full error is:
Unable to create a constant value of type 'THPT_Razor.Models.WinType'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
and I am using EF v4.0.
The commented lines are the offending code and the work around is the "For loop"
Thank you in advance.
List<WinType> _atype = db.WinTypes.Where(wt => wt.IsWin == false).ToList();
List<WinType> _wtype = db.WinTypes.Where(wt => wt.IsWin == true).ToList();
string test = _wtype.Where(wt => wt.Value ==0).Select(wt => wt.Description).SingleOrDefault();
List<WinCheckDetails> wcd = db.Wins.Include("UserProfiles").Where(w => w.venueLogId == logid).Select(w => new WinCheckDetails
{
//awarddesc = w.atypeid.HasValue ? _atype.Where( wt=> wt.Value == w.atypeid).Select(wt => wt.Description).SingleOrDefault():string.Empty,
//windesc = _wtype.Where(wt => wt.Value == w.typeid).Select(wt => wt.Description).Single(),
atypeid = w.atypeid,
typeid = w.typeid,
WinId = w.WinId,
other = w.other,
posterid = w.posterid,
confirmed = w.confirmed,
posttime = w.posttime,
game = w.game,
playerid = w.UserProfile.PlayerID,
firstname = w.UserProfile.FirstName,
lastname = w.UserProfile.LastName,
fullname = w.UserProfile.FirstName + " " + w.UserProfile.LastName
}).OrderBy(o => o.game).ToList();
foreach (WinCheckDetails wc in wcd)
{
wc.awarddesc = _atype.Where(wt => wt.Value == wc.atypeid).Select(wt => wt.Description).SingleOrDefault();
wc.windesc = _wtype.Where(wt => wt.Value == wc.typeid).Select(wt => wt.Description).SingleOrDefault();
}
_atype and _wtype are lists of WinType in memory because you are applying ToList() to the queries. With respect to database queries they are collections of constant values because to perform the query in the database they have to be transmitted to the database server as the values they are in memory. EF doesn't support to transfer such constant values or collections of values from memory to the database unless they are values of primitive types (int for example). That's the reason why you get an exception.
Did you try to use _atype and _wtype as IQueryable instead of lists:
IQueryable<WinType> _atype = db.WinTypes.Where(wt => !wt.IsWin);
IQueryable<WinType> _wtype = db.WinTypes.Where(wt => wt.IsWin);
List<WinCheckDetails> wcd = db.Wins
.Where(w => w.venueLogId == logid)
.Select(w => new WinCheckDetails
{
awarddesc = w.atypeid.HasValue
? _atype.Where(wt=> wt.Value == w.atypeid)
.Select(wt => wt.Description).FirstOrDefault()
: string.Empty,
windesc = _wtype.Where(wt => wt.Value == w.typeid)
.Select(wt => wt.Description).FirstOrDefault(),
// ... (unchanged)
}).OrderBy(o => o.game).ToList();
I have removed the Include because it will be ignored anyway when you perform a projection with Select. Also I have replaced SingleOrDefault and Single by FirstOrDefault because both are not supported in a projection (and First neither), only FirstOrDefault is supported.
I am not sure if that will work. But it should remove your exception (but maybe you'll get another one...).

Zend DB returning NULL value

I have the following query that extends from Zend_DB_Table_Abstract
$select = $this->select()
->from('expense_details',
array('SUM(expense_details_amount) AS total'))
->where('YEAR(expense_details_date) = ?', '2010')
->where('MONTH(expense_details_date) = ?', '01')
->where('expense_details_linkemail = ?', 'xxxx#gmail.com');
However it returning a NULL value despite its "equivalent" returning the desired value
SELECT SUM(expense_details_amount) AS total FROM expense_details
WHERE
YEAR(expense_details_date) = '2010'
AND MONTH(expense_details_date) = '01'
AND expense_details_linkemail = 'xxxx#gmail.com'
Is my Zend_DB_Table construct above correct?
One thing that might be a problem is the 'AS' statement within this string literal.
array('SUM(expense_details_amount) AS total'))
Try changing it to this:
array('total' => 'SUM(expense_details_amount)'))
I believe this is how Zend_Db_Select handles AS.
After searching hard for a solution I found where the problem is.
I changed
$value = $this->fetchAll($select);
$data[] = $value->total;
to
$value = $this->fetchRow($select);
$data[] = $value->total;

Grouping WHERE clauses with Zend_Db_Table_Abstract

Does anyone know of a way to group where clauses with Zend_Db? Basically I have this query
$sql = $table->select()
->where('company_id = ?', $company_id)
->where('client_email = ?', $client_email)
->orWhere('client_email_alt = ?', $client_email);
Which is giving me this:
SELECT `clients`.* FROM `clients` WHERE (company_id = '1') AND (client_email = 'email#address.com') OR (client_email_alt = 'email#address.com')
But I need it to give me this, where the OR statement is grouped:
SELECT `clients`.* FROM `clients` WHERE (company_id = '1') AND ((client_email = 'email#address.com') OR (client_email_alt = 'email#address.com'))
In order to achieve this, you have to construct the grouped clause within a single call to the where method.
If both values of conditions are the same, you can do this:
$select->where('client_email = ? OR client_email_alt = ?', $client_email)
If there are multiple placeholders within the string, the DB adapter's quoteInto method will replace all placeholders with the provided value.
If you need to group an OR with different values for each field, you have to manually quote the values. It's a bit more complex:
$select->where(
$db->quoteInto('client_email = ?', $email1) . ' OR ' . $db->quoteInto('client_email_alt = ?', $email2)
); // $db is your instance of Zend_Db_Adapter_*
// You can get it from a Zend_Db_Table_Abstract
//subclass by calling its getAdapter() method
You can use getPart() to get WHERE statement and then connect sub-queries.
$select->where('client_email = ?', $client_email)
->orWhere('client_email_alt = ?', $client_email);
$subquery = $select->getPart(Zend_Db_Select::WHERE);
$select ->reset(Zend_Db_Select::WHERE);
$select ->where('company_id = ?', $company_id)
->where(implode(' ',$subquery));
For Zend Framework Version 2, things differ a bit:
See http://framework.zend.com/apidoc/2.2/classes/Zend.Db.Sql.Predicate.Predicate.html#nest
$table->select()
->where(['company_id'=> $company_id])
->nest
->where('client_email = ?', $client_email)
->or
->where('client_email_alt = ?', $client_email)
->unnest();
works fine and feels much cleaner than the ZF1 methods.
I needed to combine AND/OR statements but including OR statements conditionally, adding them only in some cases. This solution is an adaptation of what I did, based on small modifications of the accepted answer.
$sql = $table->select()
->where('company_id = ?', $company_id);
$orWhereClauses = [];
// We could add a conditional statement to add this statement
$orWhereClauses[] = $db->quoteInto('client_email = ?', $email1);
// Same applies to this statement
$orWhereClauses[] = $db->quoteInto('client_email_alt = ?', $email2);
$sql->where(implode(" OR ", $orWhereClauses));
In first you can generate subquery, then get "WHERE" part and insert into main query
$subquery = $db->select();
$subquery->orWhere('a>10');
$subquery->orWhere('b<20');
etc..
$subquery = $subquery->getPart(Zend_Db_Select::WHERE);
$select->where(implode(' ',$subquery));