Doctrine2 many to many not in - doctrine-orm

I have 2 entities: Tax and Category.
A Tax can have many categories and a Category can be in many taxes.
This is how I have defined the categories property in my Tax entity.
/**
* #ORM\ManyToMany(targetEntity="\My\Bundle\Entity\Category")
* #ORM\JoinTable(name="taxes_categories",
* joinColumns={#ORM\JoinColumn(name="tax_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")}
* )
**/
protected $categories;
What I'm trying to fetch is all the categories that are not associated to a particular tax.
In SQL i would do something like
SELECT * FROM category WHERE id NOT IN (SELECT category_id FROM taxes_categories WHERE tax_id = ?)
How can I make a query like that with Doctrine?

You don't have to worry about the join table. Doing a join between Category and Tax will do the job. I would do the DQL query using the QueryBuilder. Something like that:
$qb = $entityManager->createQueryBuilder();
$qb->select( array( 'C' ) )
->from( '\My\Bundle\Entity\Category', 'C' )
->join( 'C.taxes', 'T' )
->where( $qb->expr()->neq( 'T.id', ':id' ) )
->setParameter( 'id', $id );
You ask about fetching all categories, so I'd work from Category entity. I assume you have a taxes property defined in it.

Related

Target non-existing entity in querybuilder (ManyToMany relationship)

I can't seem to target the JoinTable 'product_features' in a query builder:
/**
* #ORM\ManyToMany(targetEntity="AttributeOption", cascade={"persist"})
* #ORM\JoinTable(name="product_features",
* joinColumns={#ORM\JoinColumn(name="product_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="attribute_option_id", referencedColumnName="id")}
* )
*/
protected $features;
I did discover that you can join a non-related table but you need to call it by the entity in order to do so. Am I right? There is no entity since it's just a table generated by the ManyToMany mapping.
This is what I've tried but it does target the targetEntity 'AttributeOption':
$query
->innerJoin('p.features', 'f')
->setParameter('filters', $filters)
->andWhere('f.id IN (:filters)');
I really need the product_features table in my query. Please, help me out!
Thanks in advance!
Regards,
Demi

How to get products available quantity (Odoo v8 and v9)

I need to get products available quantity from odoo stock.
There are several models I stock_quant, stock_move, stock_location.
What I am trying to achieve are two things:
Products total available quantity
Products available quantity based on location
Can anyone please guide me?
Stock related fields are defines in products (functional field) and directly from the product you can get the stock for all warehouses / locations or for individual location / warehouse.
Example:
For all warehouses / locations
product = self.env['product.product'].browse(PRODUCT_ID)
available_qty = product.qty_available
For individual location / warehouse (WAREHOUSE_ID / LOCATION_ID should be replaced by actual id)
product = self.env['product.product'].browse(PRODUCT_ID)
available_qty = product.with_context({'warehouse' : WAREHOUSE_ID}).qty_available
available_qty = product.with_context({'location' : LOCATION_ID}).qty_available
Other fields are also there.
Forecasted Stock => virtual_available
Incoming Stock => incoming
Outgoing Stock => outgoing
You can access all those fields in similar manner. If you will not pass any warehouse / location in context then it will returns the stock of the all warehouses together.
For more details you may refer product.py in stock module.
Solution:
#api.onchange('product_id','source_location')
def product_qty_location_check(self):
if self.product_id and self.source_location:
product = self.product_id
available_qty = product.with_context({'location' : self.source_location.id}).qty_‌​available
print available_qty
For Odoo 8,9 and 10:
with
uitstock as (
select
t.name product, sum(product_qty) sumout, m.product_id, m.product_uom
from stock_move m
left join product_product p on m.product_id = p.id
left join product_template t on p.product_tmpl_id = t.id
where
m.state like 'done'
and m.location_id in (select id from stock_location where complete_name like '%Stock%')
and m.location_dest_id not in (select id from stock_location where complete_name like '%Stock%')
group by product_id,product_uom, t.name order by t.name asc
),
instock as (
select
t.list_price purchaseprice, t.name product, sum(product_qty) sumin, m.product_id, m.product_uom
from stock_move m
left join product_product p on m.product_id = p.id
left join product_template t on p.product_tmpl_id = t.id
where
m.state like 'done' and m.location_id not in (select id from stock_location where complete_name like '%Stock%')
and m.location_dest_id in (select id from stock_location where complete_name like '%Stock%')
group by product_id,product_uom, t.name, t.list_price order by t.name asc
)
select
i.product, sumin-coalesce(sumout,0) AS stock, sumin, sumout, purchaseprice, ((sumin-coalesce(sumout,0)) * purchaseprice) as stockvalue
from uitstock u
full outer join instock i on u.product = i.product

Symfony One-To-Many, unidirectional with Join Table query

I have some One-To-Many, unidirectional with Join Table relationships in a Symfony App which I need to query and I can't figure out how to do that in DQL or Query Builder.
The Like entity doesn't have a comments property itself because it can be owned by a lot of different types of entities.
Basically I would need to translate something like this:
SELECT likes
FROM AppBundle:Standard\Like likes
INNER JOIN comment_like ON comment_like.like_id = likes.id
INNER JOIN comments ON comment_like.comment_id = comments.id
WHERE likes.created_by = :user_id
AND likes.active = 1
AND comments.id = :comment_id
I've already tried this but the join output is incorrect, it selects any active Like regardless of its association with the given comment
$this->createQueryBuilder('l')
->select('l')
->innerJoin('AppBundle:Standard\Comment', 'c')
->where('l.owner = :user')
->andWhere('c = :comment')
->andWhere('l.active = 1')
->setParameter('user', $user)
->setParameter('comment', $comment)
I see 2 options to resolve this:
Make relation bi-directional
Use SQL (native query) + ResultSetMapping.
For the last option, here is example of repository method (just checked that it works):
public function getLikes(Comment $comment, $user)
{
$sql = '
SELECT l.id, l.active, l.owner
FROM `like` l
INNER JOIN comment_like ON l.id = comment_like.like_id
WHERE comment_like.comment_id = :comment_id
AND l.active = 1
AND l.owner = :user_id
';
$rsm = new \Doctrine\ORM\Query\ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata(Like::class, 'l');
return $this->_em->createNativeQuery($sql, $rsm)
->setParameter('comment_id', $comment->getId())
->setParameter('user_id', $user)
->getResult();
}
PS: In case of Mysql, 'like' is reserved word. So, if one wants to have table with name 'like' - just surround name with backticks on definition:
* #ORM\Table(name="`like`")
I find the Symfony documentation very poor about unidirectional queries.
Anyway I got it working by using DQL and sub-select on the owning entity, which is certainly not as fast. Any suggestion on how to improve that is more than welcomed!
$em = $this->getEntityManager();
$query = $em->createQuery('
SELECT l
FROM AppBundle:Standard\Like l
WHERE l.id IN (
SELECT l2.id
FROM AppBundle:Standard\Comment c
JOIN c.likes l2
WHERE c = :comment
AND l2.owner = :user
AND l2.active = 1
)'
)
->setParameter('user', $user)
->setParameter('comment', $comment)
;

Table view OR temporary table in Doctrine + symfony 2.3

I was trying to implement bayesian average logic in doctrine and symfony.
I have basic native Mysql query like this:
Create View `ratings` AS
SELECT
restaurant_id,
(SELECT count(restaurant_id) FROM ratings) / (SELECT count(DISTINCT restaurant_id) FROM ratings) AS avg_num_votes,
(SELECT avg(rating) FROM ratings) AS avg_rating,
count(restaurant_id) as this_num_votes,
avg(rating) as this_rating
FROM
ratings
GROUP BY
restaurant_id
SELECT
restaurant_id,
((avg_num_votes * avg_rating) + (this_num_votes * this_rating)) / (avg_num_votes + this_num_votes) as real_rating
FROM `ratings`
This query creates table view from where we are retrieving records.
From some documents I came to know that we can't create view in Doctrine. So another option is to create temporary table. How we can create temp table with different structure.
Referring : http://shout.setfive.com/2013/11/21/doctrine2-using-resultsetmapping-and-mysql-temporary-tables/
// Add the field info for each column/field
foreach( $classMetadata->fieldMappings as $id => $obj ){
$rsm->addFieldResult('u', $obj["columnName"], $obj["fieldName"]);
// I want to crate new fields to store avg rating etc.
}
How we can implement this in Doctrine and Symfony?

Doctrine 2 edit DQL in entity

I have several database tables with 2 primary keys, id and date. I do not update the records but instead insert a new record with the updated information. This new record has the same id and the date field is NOW(). I will use a product table to explain my question.
I want to be able to request the product details at a specific date. I therefore use the following subquery in DQL, which works fine:
WHERE p.date = (
SELECT MAX(pp.date)
FROM Entity\Product pp
WHERE pp.id = p.id
AND pp.date < :date
)
This product table has some referenced tables, like category. This category table has the same id and date primary key combination. I want to be able to request the product details and the category details at a specific date. I therefore expanded the DQL as shown above to the following, which also works fine:
JOIN p.category c
WHERE p.date = (
SELECT MAX(pp.date)
FROM Entity\Product pp
WHERE pp.id = p.id
AND pp.date < :date
)
AND c.date = (
SELECT MAX(cc.date)
FROM Entity\ProductCategory cc
WHERE cc.id = c.id
AND cc.date < :date
)
However, as you can see, if I have multiple referenced tables I will have to copy the same piece of DQL. I want to somehow add these subqueries to the entities so that every time an entity is called it adds this subquery.
I have thought of adding this in a __construct($date) or some kind of setUp($date) method, but I'm kind of stuck here. Also, would it help to add #Id to Entity\Product::date?
I hope someone can help me. I do not expect a complete solution, one step in a good direction would be very much appreciated.
I think I've found my solution. The trick was (first, to update to Doctrine 2.2 and) using a filter:
namespace Filter;
use Doctrine\ORM\Mapping\ClassMetaData,
Doctrine\ORM\Query\Filter\SQLFilter;
class VersionFilter extends SQLFilter {
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) {
$return = $targetTableAlias . '.date = (
SELECT MAX(sub.date)
FROM ' . $targetEntity->table['name'] . ' sub
WHERE sub.id = ' . $targetTableAlias . '.id
AND sub.date < ' . $this->getParameter('date') . '
)';
return $return;
}
}
Add the filter to the configuration:
$configuration->addFilter("version", Filter\VersionFilter");
And enable it in my repository:
$this->_em->getFilters()->enable("version")->setParameter('date', $date);