My problem is simple. I have two tables
transaction_bodies
------------------
body_id
full_name
and the other one is
transaction_accounts
--------------------
account_id
body_id
account_name
Relation is one to many. One body can have multiple accounts. I am trying to create a query that counts the accounts that bodies have.
I tried this
SELECT *
FROM
(
SELECT count(*) as trans, tb.full_name
FROM transaction_accounts ta
LEFT JOIN transaction_bodies tb
ON tb.body_id = ta.body_id
) as row;
But this doesn't give the right result. Can anyone help me out with this?
And if can provide how to write sub-queries in Laravel that would be a appreciated much.
Try this :
$result = DB::table('transaction_bodies')
->leftJoin('transaction_accounts as
ta','transaction_bodies.body_id','ta.body_id')
->select(DB::raw('count(ta.account_id) AS trans'),'transaction_bodies.full_name')
->groupBy('transaction_bodies.body_id')
->get();
You can do it with LEFT JOIN, e.g.:
SELECT tb.body_id, COUNT(ta.*)
FROM transaction_bodies LEFT JOIN transaction_accounts ta
ON tb.body_id = ta.body_id
GROUP BY tb.body_id;
With a simple LEFT JOIN you can achieve it like
SELECT tb.full_name, COUNT(account_id) as accounts
FROM transaction_bodies tb LEFT JOIN transaction_accounts ta
ON tb.body_id = ta.body_id
GROUP BY tb.body_id;
In Laravel you can do it like with model
$accounts = Transaction_body::leftJoin('transaction_accounts as ta','transaction_bodies.body_id','ta.body_id')->groupBy('transaction_bodies.body_id')->get();
without model
$accounts = DB::table('transaction_bodies')->leftJoin('transaction_accounts as ta','transaction_bodies.body_id','ta.body_id')->groupBy('transaction_bodies.body_id')->get();
/**
* Class Body
*/
class Body extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'transaction_bodies';
/**
* Get the accounts for the Transaction Body.
*/
public function accounts()
{
return $this->hasMany(Account::class);
}
}
/**
* Class Account
*/
class Account extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'transaction_accounts';
/**
* Get the body that owns the account.
*/
public function body()
{
return $this->belongsTo(Body::class);
}
}
//usage
$accounts = Body::find(1)->accounts;
https://laravel.com/docs/5.4/eloquent-relationships#one-to-many
Related
The below query correctly gives me a list of Enabled Configurable items, where ALL associated simple items are disabled. How can I amend this to update the 'status' attribute value to '2' for the configurable items in this list. (They have no child items so need to be disabled)
I appreciate I have mixed together approaches of table naming and referencing. I'm new to this and have combined elements of different solutions.
SELECT `mgic_catalog_product_entity`.`entity_id` FROM (((`mgic_eav_attribute`
join `mgic_catalog_product_entity_int` on ((`mgic_eav_attribute`.`attribute_id` = `mgic_catalog_product_entity_int`.`attribute_id`)))
join `mgic_catalog_product_entity` on ((`mgic_catalog_product_entity_int`.`entity_id` = `mgic_catalog_product_entity`.`entity_id`)))
join `mgic_cataloginventory_stock_item` on ((`mgic_catalog_product_entity_int`.`entity_id` = `mgic_cataloginventory_stock_item`.`product_id`)))
WHERE `mgic_catalog_product_entity`.`type_id` = 'configurable' AND ((`mgic_eav_attribute`.`attribute_code` = 'status') and
(`mgic_catalog_product_entity_int`.`value` = 2)) AND NOT EXISTS(
SELECT *
FROM mgic_catalog_product_super_link cpsl
INNER JOIN mgic_catalog_product_entity_int cpei ON cpei.entity_id = cpsl.product_id
WHERE
parent_id = `mgic_catalog_product_entity`.`entity_id`
AND cpei.attribute_id = 97
AND cpei.value = 1
);
Fetch IDs of configurables with disabled simples
As status attribute will always have id 97 you could use the following SQL:
SELECT entity_id FROM `catalog_product_entity` cpe
WHERE `type_id` = 'configurable' AND NOT EXISTS(
SELECT *
FROM catalog_product_super_link cpsl
INNER JOIN catalog_product_entity_int cpei ON cpei.entity_id = cpsl.product_id
WHERE
parent_id = cpe.entity_id
AND cpei.attribute_id = 97
AND cpei.value = 1
);
Disable products by IDs
To answer your updated question I would suggest you fetching all entity ids and then just use Magento models to change status of each product like suggested here https://magento.stackexchange.com/questions/152263/how-to-disable-enable-a-product-programatically-in-magento2 .
For example:
<?php
class Example
{
/**
* #var \Magento\Catalog\Model\ProductRepository
*/
protected $productRepository;
/**
* #var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
*/
protected $productCollectionFactory;
/**
* #param \Magento\Catalog\Model\ProductRepository $productRepository
*/
public function __construct(
\Magento\Catalog\Model\ProductRepository $productRepository,
\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory
) {
$this->productRepository = $productRepository;
$this->productCollectionFactory = $productCollectionFactory;
}
/**
* Disable all products by IDs
* #throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function disableProducts($productIds)
{
$productCollection = $this->productCollectionFactory()->create();
$productCollection->addAttributeToFilter('type_id', array('eq' => 'configurable'))
->setPageSize(999);
$productCollection->getSelect()
->where('NOT EXISTS(
SELECT *
FROM catalog_product_super_link cpsl
INNER JOIN catalog_product_entity_int cpei ON cpei.entity_id = cpsl.product_id
WHERE
parent_id = e.entity_id
AND cpei.attribute_id = 97
AND cpei.value = 1
)');
foreach ($productCollection as $p) {
$product = $this->productRepository->getById(
$p->getId(),
true /* edit mode */,
0 /* global store*/,
true/* force reload*/
);
$product->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED);
$this->productRepository->save($product);
}
}
}
I have this SQL query for MySQL which works fine. But I need to rewrite it using query builder and need to avoid DB::raw() completely because development database is different from production. I know far from ideal, but unfortunately it is what it is.
SELECT athletes.*,
(
SELECT performance
FROM performances
WHERE athletes.id = performances.athlete_id AND performances.event_id = 1
ORDER BY performance DESC
LIMIT 0,1
) AS personal_best
FROM athletes
ORDER BY personal_best DESC
Limit 0, 100
And I'm struggling how to rewrite the personal_best part. I have table of performances for athletes and I need to select only the best performance for each athletes as his personal best.
I was trying to search for answer but all of the answers I found included raw adding raw SQL.
Any ideas or hint would be much appreciated.
Thank you in advance!
So I accepted I might have to use Eloquent for this, but still having trouble to progress. Heres my code:
class Athlete extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'athletes';
/**
* The primary key associated with the table.
*
* #var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
/**
* Get the performances for the Athelete post.
*
* #return HasMany
*/
public function performances()
{
return $this->hasMany('App\EloquentModels\Performance', 'athlete_id');
}
}
class Performance extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'performances';
/**
* The primary key associated with the table.
*
* #var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
}
Create a new connection at database.php like mysql_dev for development parameters.
DB::connection('mysql_dev')->table('athletes')
->leftJoin('performances','athletes.id','performances.athlete_id')
->where('performances.event_id',1)
->groupBy('athletes.id')
->orderByDesc('personal_best')
->select('athletes.*',DB::raw('MAX(performances.performance) AS personal_best')
->paginate(100);
try like this without raw,
DB::connection('mysql_dev')->table('athletes')
->leftJoin('performances','athletes.id','performances.athlete_id')
->where('performances.event_id',1)
->groupBy('athletes.id')
->orderByDesc('performances.performance')
->select('athletes.*','performances.performance'
->paginate(100);
If you are using raw SQL just do MAX for performance for each athlete using GROUP BY.
SELECT athletes.*, MAX(performance) AS personal_best
FROM athletes
INNER JOIN performances ON athletes.id = performances.athlete_id AND performances.event_id = 1
GROUP BY athletes.id
ORDER BY personal_best DESC
LIMIT 0, 100
Laravel Query Builder:
DB::table('athletes')
->join('performances', 'athletes.id', '=', 'performances.athlete_id')
->where('performances.event_id', '=', 1)
->groupBy('athletes.id')
->orderBy('personal_best', 'desc')
->select('athletes.*',DB::raw('MAX(performance) AS personal_best')
->limit(100);
Doc says that we can do max(personal_best) but not sure how to use it with group by.
I'm afraid you can't avoid DB::raw in Query Builder but you can use eloquent model for the same, as answered by Shaielndra Gupta.
For that you can create model and relationship.
1. Create Model:
php artisan make:model Athelete
php artisan make:model Performance
2. Create relationship between Athelete and Perforamnce.
Update Athelete.php
/**
* Get the performances for the Athelete post.
*/
public function performances()
{
return $this->hasMany('App\Performance');
}
3. Get data(didn't verify by myself)
$data = Athelete::with('performances',function ($query) use ($eventId){
$query->max('performance')
$query->where('event_id',$eventId)
$query->orderBy('performance');
})->get();
Reference:
Laravel Model
Laravel Relationship
You can use like below.
$sql1 = "(
SELECT performance
FROM performances
WHERE athletes.id = performances.athlete_id AND performances.event_id = 1
ORDER BY performance DESC
LIMIT 0,1
) AS personal_best";
$sql2 = "SELECT athletes.*,$sql1
FROM athletes
ORDER BY personal_best DESC
Limit 0, 100";
$result = DB::select($sql2);
you can user Eloquent ORM like this
$data = Athelete::with('performances',function ($query) use ($eventId){
$query->max('performance')
$query->where('event_id',$eventId)
$query->orderBy('performance');
})->get()
In my Padel project on Symfony2 I have a Competition - Registration relationship
On the "Competition" entity:
/**
* #ORM\OneToMany(targetEntity="Registration", mappedBy="competition")
*/
protected $registrations;
On the "Registration" entity:
/**
* #ORM\ManyToOne(targetEntity="PadelSchedule\UserBundle\Entity\User", inversedBy="registrations")
* #ORM\JoinColumn(name="idPlayer", referencedColumnName="id")
*/
private $player;
/**
* #ORM\ManyToOne(targetEntity="Competition", inversedBy="registrations")
* #ORM\JoinColumn(name="idCompetition", referencedColumnName="id")
*/
private $competition;
Something like this. What I need to do is, having the id of the player, get a list of the competitions on which this player is registered.
The close thing that I have get is using a join with this query:
$qb = $em->createQueryBuilder()
->select('r, c')
->from('PadelScheduleMainBundle:Registration', 'r')
->leftJoin('r.competition', 'c')
->where('r.player = :idPlayer')
->setParameter('idPlayer', $idPlayer);
But as it seems by the error I get, what I think I get through this is a list of Registrations with the competition joined, but I need for my view is a list of "Competition" objects.
Any help? Thanks!
Since you select from your Registration entity you get a result of Registrations. Maybe you have better luck using a query like this:
$em->createQueryBuilder()
->select('c')
->from('PadelScheduleMainBundle:Competition', 'c')
->innerJoin('c.registrations', 'r')
->where('r.player = :player')
->setParameter('player', $playerId)
According to your query question.. I have write this query ..Please Try with this query:
$query = $em->createQuery(
'SELECT c
FROM PadelScheduleMainBundle:Competition c
INNER JOIN c.registrations r
WHERE r.player = :idPlayer')
->setParameter('idPlayer', $idPlayer);
I built the following Parts of my Entities :
/**
* #var Application_Model_Ticket
*
* #OneToMany(targetEntity="Application_Model_Ticket",mappedBy="contacts_id")
* #JoinColumn(name="id", referencedColumnName="contacts_id")
*/
private $tickets;
public function getTickets() {
return $this->tickets;
}
/**
* #var Application_Model_Contact
*
* #ManyToOne(targetEntity="Application_Model_Contact",cascade={"persist"})
* #JoinColumn(name="contacts_id", referencedColumnName="id")
*/
private $contact;
public function getContact() {
return $this->contact;
}
When I now try the following Doctrine Code :
$this->oQueryBuilder->select('contact','ticket')
->from('Application_Model_Contact', 'contact')
->leftJoin('contact.tickets', 'ticket')
->orderBy('contact.id', 'DESC');
I get a MYSQL-Error.
The following happens :
FROM contact c0_ LEFT JOIN ORDER BY c0_.id DESC
It seems that Doctrine doesn't fill in the LEFT JOIN properly.
Does anybody have an idea, why this happens ?
UPDATE:
->leftJoin('contact.tickets', 'ticket', 'WITH','contact.id = ticket.contactsId')
Gives me
LEFT JOIN AND (c0_.id = t1_.contacts_id)
you miss the ON in your query
$this->oQueryBuilder
->select('contact','ticket')
->from('Application_Model_Contact', 'contact')
->leftJoin('contact', 'on contact.ticket=Application_Model_Contact.field')
->orderBy('contact.id', 'DESC');
where Application_Model_Contact.field replace it for the match field to apply the JOIN
so your query should be like:
....FROM contact c0_ LEFT JOIN ON table1.field=table2.field ORDER BY c0_.id DESC
Source: http://oldforum.symfony-project.org/index.php/m/69061/
Mysql Join: http://dev.mysql.com/doc/refman/5.0/en/join.html
Try this:
$this->oQueryBuilder->select('contact','ticket')
->from('Application_Model_Contact', 'contact')
->leftJoin('contact.tickets', 'ticket', 'WITH', 'contact.id = ticket.contacts_id')
->orderBy('contact.id', 'DESC');
How to get SQL_CALC_FOUND_ROWS with Zend\Db\TableGateway without using direct low level queries with raw SQL?
class ProductTable {
protected $tableGateway;
/**
* Set database gateway
*
* #param TableGateway $tableGateway - database connection
* #return void
*/
public function __construct(TableGateway $tableGateway) {
$this->tableGateway = $tableGateway;
}
/**
* Fetch all products
*
* #param integer $page - page of records
* #param integer $perpage - records per page
* #return void
*/
public function fetchAll($page = 1, $perpage = 18) {
return $this->tableGateway->select(function (Select $select) use ($page, $perpage) {
$select
->limit($perpage)
->offset(($page - 1) * $perpage);
});
}
}
I wish to get total number of records in a same query used in fetchAll.
Looks like Zend Framework 2.1.4 has support to specify a quantifier. This enables you to use the SQL_CALC_FOUND_ROWS in a select object. One thing I did find tricky to work around is that Zend's Zend\Db\Sql\Select class will not generate the correct SQL for you if you did not specify a table. This becomes and issue when executing the subsequent select to retrieve the FOUND_ROWS(). I've updated your code below to include what I would use. I've merge my project implementation into your code, so if something does not work, its probably because I mistype something, but overall it works for me (not as desirable as I would want).
use Zend\Db\Sql\Expression;
use Zend\Db\Sql\Select;
class ProductTable {
protected $tableGateway;
/**
* Set database gateway
*
* #param TableGateway $tableGateway - database connection
* #return void
*/
public function __construct(TableGateway $tableGateway) {
$this->tableGateway = $tableGateway;
}
/**
* Fetch all products
*
* #param integer $page - page of records
* #param integer $perpage - records per page
* #return void
*/
public function fetchAll($page = 1, $perpage = 18) {
$result = $this->tableGateway->select(function (Select $select) use ($page, $perpage) {
$select
->quantifier(new Expression('SQL_CALC_FOUND_ROWS'))
->limit($perpage)
->offset(($page - 1) * $perpage);
});
/* retrieve the sql object from the table gateway */
$sql = $this->tableGateway->getSql();
/* create an empty select statement passing in some random non-empty string as the table. need this because Zend select statement will
generate an empty SQL if the table is empty. */
$select = new Select(' ');
/* update the select statement specification so that we don't incorporate the FROM clause */
$select->setSpecification(Select::SELECT, array(
'SELECT %1$s' => array(
array(1 => '%1$s', 2 => '%1$s AS %2$s', 'combinedby' => ', '),
null
)
));
/* specify the column */
$select->columns(array(
'total' => new Expression("FOUND_ROWS()")
));
/* execute the select and extract the total */
$statement = $sql->prepareStatementForSqlObject($select);
$result2 = $statement->execute();
$row = $result2->current();
$total = $row['total']';
/* TODO: need to do something with the total? */
return $result;
}
}