i want to perform a Find operation on Entity Results.
$stores = c2gr($this, 'EducateToolsBundle:Stores')->findBy(['selectOption' => true, 'center_type_id' => 9])
i want to do center_type_id != 9
Just use a DQL or QueryBuilder.
$repository
->createQueryBuilder('s')
->where('s.selectOption = :selectOption')
->andWhere('s.center_type_id <> :center_type_id')
->setParameters([
'selectOption' => true
'center_type_id' => 9,
])
->getQuery()
->getResult();
You should be able to achieve that by using Criteria class. Something like this should work:
use Doctrine\Common\Collections\Criteria;
$criteria = Criteria::create();
$criteria->where($criteria->expr()->neq('center_type_id', 9));
$criteria->andWhere($criteria->expr()->eq('selectOption', true));
$entityRepository = $this->getDoctrine()->getRepository('C2EducateToolsBundle:Stores');
$result = $entityRepository->matching($criteria);
NOTE: above will work if you are in class which extends Symfony\Bundle\FrameworkBundle\Controller\Controller since it is using getDoctrine method. If you are not in such class you should either inject EntityManager or get it from service container.
For more details you should check this answer
Related
I have a table message_thread:
id
sender_id
recipient_id
I want to declare a relation in my User model that will fetch all message threads as follows:
SELECT *
FROM message_thread
WHERE sender_id = {user.id}
OR recipent_id = {user.id}
I have tried the following:
public function getMessageThreads()
{
return $this->hasMany(MessageThread::className(), ['sender_id' => 'id'])
->orWhere(['recipient_id' => 'id']);
}
But it generates an AND query. Does anyone know how to do this?
You cannot create regular relation in this way - Yii will not be able to map related records for eager loading, so it not supporting this. You can find some explanation int this answer and related issue on GitHub.
Depending on use case you may try two approach to get something similar:
1. Two regular relations and getter to simplify access
public function getSenderThreads() {
return $this->hasMany(MessageThread::className(), ['sender_id' => 'id']);
}
public function getRecipientThreads() {
return $this->hasMany(MessageThread::className(), ['recipient_id' => 'id']);
}
public function getMessageThreads() {
return array_merge($this->senderThreads, $this->recipientThreads);
}
In this way you have two separate relations for sender and recipient threads, so you can use them directly with joins or eager loading. But you also have getter which will return result ofboth relations, so you can access all threads by $model->messageThreads.
2. Fake relation
public function getMessageThreads()
{
$query = MessageThread::find()
->andWhere([
'or',
['sender_id' => $this->id],
['recipient_id' => $this->id],
]);
$query->multiple = true;
return $query;
}
This is not real relation. You will not be able to use it with eager loading or for joins, but it will fetch all user threads in one query and you still will be able to use it as regular active record relation - $model->getMessageThreads() will return ActiveQuery and $model->messageThreads array of models.
Why orOnCondition() will not work
orOnCondition() and andOnCondition() are for additional ON conditions which will always be appended to base relation condition using AND. So if you have relation defined like this:
$this->hasMany(MessageThread::className(), ['sender_id' => 'id'])
->orOnCondition(['recipient_id' => new Expression('id')])
->orOnCondition(['shared' => 1]);
It will generate condition like this:
sender_id = id AND (recipent_id = id OR shared = 1)
As you can see conditions defined by orOnCondition() are separated from condition from relation defined in hasMany() and they're always joined using AND.
For this query
SELECT *
FROM message_thread
WHERE sender_id = {user.id}
OR recipent_id = {user.id}
You Can use these
$query = (new \yii\db\Query)->from("message_thread")
$query->orFilterWhere(['sender_id'=>$user_id])->orFilterWhere(['recipent_id '=>$user_id]);
I have some where condition in my model .
Its check is field active or no.
Now I need to write a join relation. But I need to remove where condition. Is it possible?
My model.
...
public static function find() {
return (new AssetgroupsQuery(get_called_class()))->active();
}
My relation
public function getAssetgroup(): \app\models\AssetgroupsQuery {
return $this->hasOne(Assetgroups::class, ['asg_id' => 'ass_group'])->andOnCondition(['asg_active' => '1'])
->viaTable('assets', ['ass_id' => 'log_ass_id',]);
}
I need to got all active assets and join, if asset is empty I need to got null fields, but
model where condition added to my current sql query and remove all fields which assets are null.
I try to add some where Condition to remove old where, but it don't work.
Can you help me?
You can reset existing conditions by using where(null).
On relation level:
public function getAssetgroup(): \app\models\AssetgroupsQuery {
return $this->hasOne(Assetgroups::class, ['asg_id' => 'ass_group'])
->andOnCondition(['asg_active' => '1'])
->where(null)
->viaTable('assets', ['ass_id' => 'log_ass_id',]);
}
Or directly on join:
$query = MyModel::find()
->joinWith([
'assetgroup' => function (ActiveQuery $query) {
$query->where(null);
},
])
I am trying to create custom query in the hasMany function. I am not getting any error, but I am not getting any data from the table inside join statement. What's is wrong?
This is the function:
return $this->hasMany(UserKeys::classname(), ['user_id' => 'id'])
->select('licences.licenceName, userKeys.*')
->from('userKeys')
->innerJoin('licences', 'licences.id = userKeys.licence_id');
Try with following syntax. It may help you.
return $this->hasMany(UserKeys::classname(), ['user_id' => 'id'])
->select('licences.licenceName, userKeys.*')
->from(['userKeys' => UserKeys::classname()])
->viaTable('licences', ['licences.id = userKeys.licence_id']);
I would like to make this query:
Session.Linq<User>().Where(u => u.Payments.Count(p => p.Date != null) > 0);
In plain English I want to get all the users that has at least one payment with the date specified.
When I run the sample code I get a System.ArgumentException with the message:
System.ArgumentException : Could not find a matching criteria info provider to: this.Id = sub.Id
Do you know a solution to this problem?
It would also be very helpful if someone could provide the same query with the NHibernate Query by Criteria API.
I'm not sure if this will work in your particular case, but I would use the .Any() extension to clean up the linq query a bit; for example:
Session.Linq<User>().Where(u => u.Payments.Any(p => p.Date != null));
I think something like it:
Customer customerAlias = null;
criteria = CurrentSession.CreateCriteria(typeof(User), () => customerAlias);
if (searchCriteria.OrdersNumber.HasValue)
{
ICriteria paymentsCriteria = criteria.CreateCriteria<Customer>(x => x.Payments);
DetachedCriteria paymentsCount = DetachedCriteria.For<Payment>();
paymentsCount.SetProjection(Projections.RowCount());
paymentsCount.Add(SqlExpression.NotNull<Payment>(x => x.Date));
paymentsCount.Add<Payment>(x => x.Customer.Id == customerAlias.Id);
paymentsCriteria.Add(Subqueries.Gt(1, paymentsCount));
}
return criteria.List<User>();
Querying child collections has been a recurring issue in our applications where we use NHibernate (via LINQ). I want to figure out how to do it right. I just tried forever to get this query to work efficiently using LINQ, and gave up. Can someone help me understand the best way to do something like this?
Model: ServiceProvider
HasMany->ServicesProvided
The gotcha here is that the HasMany is mapped as a component, so I can't directly query the ServicesProvided. For posterity's sake, here's the mapping:
public ServiceProviderMap()
{
DiscriminatorValue(ProfileType.SERVICE_PROVIDER.ID);
HasMany(p => p.ServicesProvided)
.Table("ServiceProvider_ServicesProvided")
.KeyColumn("ProfileID")
.Component(spMapping =>
{
spMapping.Map(service => service.ID)
.Not.Nullable();
})
.AsBag();
}
The query I am trying to create would return a collection of the count of each service that is provided. IE: Service1 -> 200, Service2 -> 465, etc.
I was able to get the query working using HQL, so here it is. Note that it just returns the ID of the service that is provided:
select service.ID, count(service)
from ServiceProvider as profile
inner join profile.ServicesProvided as service
group by service.ID
I was able to get the query "working" using LINQ, but it performed atrociously. Here's the code I used (warning - it's ugly).
Func<ServiceProvider, IEnumerable<ServicesProvided>> childSelector = sp => sp.ServicesProvided;
var counts = this._sessionManager.GetCurrentSession().Linq<ServiceProvider>()
.Expand("ServicesProvided")
.SelectMany(childSelector, (t, c) => new { t = t, c = c })
.Select(child => child.c)
.GroupBy(sp => sp.ID)
.Select(el => new { serviceID = el.Key, count = el.Count() });
I would love to learn how to do this correctly, please.
Short of going with HQL, the most elegant solution I can think of would be using a Criteria object. The following will give you what you need and with very low overhead:
ICriteria criteria = this._sessionManager.GetCurrentSession().CreateCriteria(typeof(ServiceProvider), "sp");
//set projections for the field and aggregate, making sure to group by the appropriate value
criteria.CreateAlias("sp.ServicesProvided", "s", JoinType.LeftOuterJoin)
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("s.ID"), "serviceID")
.Add(Projections.Count("sp.ID"), "count")
.Add(Projections.GroupProperty("s.ID")));
IList<object[]> results = criteria.List();
foreach (object[] entry in results)
{
int id = (int)entry[0], qty = (int)entry[1];
//Do stuff with the values
}