Laravel Capsule set primary key - php

I want to implement a layer between my custom framework and Eloquent, in other words I want use the Eloquent as an ORM for my framework...
I started with this https://github.com/illuminate/database
As you can see looks quite simple, after booting up the Eloquent, the Capsule become available like this:
Capsule::table('users')->where('votes', '>', 100)->get();
Same way should work this:
Capsule::table('users')->find(3);
Where the Eloquent finds user id=3
But what would be, if the table primary key set something else like user_id
I know In Laravel I can set in model primary key
public $primaryKey = 'user_id';
But unfortunately, this is not my case, so I wander if Laravel developers does not implement something for other developers to make it easy to use Eloqunet for other frameworks or at least I can't find it out?
Please help me with this.. let me know if you need me to write more details...

You can't using just the query builder. You can look at the code of Query/Builder.php and see that the find method is hard coded with id:
/**
* Execute a query for a single record by ID.
*
* #param int $id
* #param array $columns
* #return mixed|static
*/
public function find($id, $columns = ['*'])
{
return $this->where('id', '=', $id)->first($columns);
}
Just use where('user_id', $id)->first() if you don't want to follow Laravel conventions and are just using the query builder.
Beyond that, Eloquent Models should function the way you want, but you wouldn't use Capsule for those queries.

Related

Doctrine: How to order by attribute from association with Criteria

I am not able to order records by attribute from another entity.
I have class Employee, which has attribute $user, which points to class User, which have attribute $active.
Now I want to sort Employee by user.active and I am not able to do that. This is how I call the em:
/**
* #param Criteria $criteria
* #param array $order_by
* #param integer $limit
* #param integer $offset
* #return \Doctrine\Common\Collections\Collection
*/
public function findByCriteria($criteria, $order_by = null, $limit = null, $offset = null) {
$criteria->setFirstResult($offset);
$criteria->setMaxResults($limit);
$criteria->orderBy($order_by);
$result = $this->repository->matching($criteria);
return $result;
}
I inspected BaseEntityPersister.php and it seems like there is no implementation of such a thing. It just checks if user.active is an attribute of Employee class and throws
Doctrine\ORM\ORMException
Unrecognized field: user.active
I know I can do that via QueryBuilder and joins, but I want to make my code more reusable and Criteria seemed like a good choice.
Thank you for your advices!
EDIT:
If I use findBy, there is no problem with sorting field user.active. Should I consider this a limitation of matching method? It is sad, because I need to use Doctrine\Common\Collections\Criteria. I can use findBy with order and then use matching method to filter records, but I would rather do that on the database side.
EDIT 2:
I use Nette with Kdyby/Doctrine. Didn't know that user.active is implemented in Kdyby/doctrine and not in Doctrine directly. So I suppose this question won't be answered..
If you look at Kdyby/Doctrine, is extends the findBy capabilities by automatically detecting relations and performing join if needed as cane seen here by calling autoJoinOrderBy.
This is the reason why criteria does not support join, and Kdyby's findBy does.
If you want reusable way to build Doctrine queries, the Criteria does not help you with that, but there is QueryObject, which does the similar job and lets you reuse logical query parts, but using QueryBuilder.
It is not so well documented, but here are some resources:
Official documentation of QueryObject
Blog post with example usage
Kdyby/Doctrine autor's presentation on Doctrine and QueryObject (from slide 43)
Some more official information about QueryObjects

How to retrieve grandchildren through a polymorphic relation in Laravel 5.2?

My database design looks like the below diagram.
A course has many modules
Modules morphs to Scorm and other 2 tables (not used in this example)
A scorm Morph one Module and has Many Scoes
I am trying to access from my course model all the scoes related using:
$course->scoes.
I am using Laravel 5.2, using the Eloquent relations I know this is not possible. After few tests, using the query builder I actually can return the correct data, but, they come back as being an instance of Module::class instead of Scorm, and Sco.
This is the code I have now.
Thanks,
public function modules() {
return $this->hasMany(Module::class);
}
public function scorms(){
return $this->modules()->where('moduleable_type','=','scorms');
}
public function scoes(){
return $this->scorms()->select('scoes.*')
->join('scoes','modules.moduleable_id','=','scoes.scorm_id');
}
I'm not sure but this may be exactly what you want.
https://laravel.com/docs/5.2/eloquent-relationships#has-many-through
I found a way to do what I was trying to do. This way makes only 2 queries to the db and it's located in the Sco Model.
//returns a Course Object.
$sco->course()
/**
* Get the course
* #return App\Course|null
*/
public function Course(){
$o = $this
->join('scorms','scoes.scorm_id','=','scorms.id')
->join('modules',function($join){
$join
->on('moduleable_id','=','scorms.id')
->where('moduleable_type','=','scorms');
})
->join('courses','modules.course_id','=','courses.id')
->select('courses.id as course_id')
->first();
if(!$o) return null;
return Course::find($o->course_id);
}

laravel Eloquent one to many two relationship. OpenCart table style

How to implement information and information_description tables in laravel Eloquent Model ? Some how it need to set language, because a title should be a one record.
$information = App\Information::find(1);
$information->title
tables structure
You can review how to do this, and plenty more, by reading the documents by Laravel. They are a great help and this particular question has an example and everything. Having said that, I'll help with getting you started.
Define a relationship in either model, information or information_description, or both. For brevity, I'll use information only.
Pass the foreign_key and local_key in the hasMany() relationship since it differs from Laravel's default behavior.
So we have a model that now looks like:
class Information extends Model
{
/**
* Get the descriptions for the Information model.
* Note the 2nd and 3rd arguments in the method
* which define foreign_key and local_key.
*/
public function description()
{
return $this->hasMany('App\InformationDescription', 'information_id', 'information_id');
}
}
Now that you have the relationship defined, you can perform your query.
// Get the description for the information
$information = App\Information::find(1)->description;
// Iterate over the results
foreach ($information as $description) {
$description->title;
}
The table naming convention used is a little odd, but if I understand it correctly, this will work. Hope it helps.

Check if belongsToMany relation exists - Laravel

Two of my tables (clients and products) have a ManyToMany relation using Laravel's blongToMany and a pivot table.
Now I want to check if a certain client has a certain product.
I could create a model to check in the pivot table but since Laravel does not require this model for the belongsToMany method I was wondering if there is another way to check if a certain relationship exists without having a model for the pivot table.
I think the official way to do this is to do:
$client = Client::find(1);
$exists = $client->products->contains($product_id);
It's somewhat wasteful in that it'll do the SELECT query, get all results into a Collection and then finally do a foreach over the Collection to find a model with the ID you pass in. However, it doesn't require modelling the pivot table.
If you don't like the wastefulness of that, you could do it yourself in SQL/Query Builder, which also wouldn't require modelling the table (nor would it require getting the Client model if you don't already have it for other purposes:
$exists = DB::table('client_product')
->whereClientId($client_id)
->whereProductId($product_id)
->count() > 0;
The question is quite old but this may help others looking for a solution:
$client = Client::find(1);
$exists = $client->products()->where('products.id', $productId)->exists();
No "wastefulness" as in #alexrussell's solution and the query is more efficient, too.
Alex's solution is working one, but it will load a Client model and all related Product models from DB into memory and only after that, it will check if the relationship exists.
A better Eloquent way to do that is to use whereHas() method.
1. You don't need to load client model, you can just use his ID.
2. You also don't need to load all products related to that client into memory, like Alex does.
3. One SQL query to DB.
$doesClientHaveProduct = Product::where('id', $productId)
->whereHas('clients', function($q) use($clientId) {
$q->where('id', $clientId);
})
->count();
Update: I did not take into account the usefulness of checking multiple relations, if that is the case then #deczo has a way better answer to this question. Running only one query to check for all relations is the desired solution.
/**
* Determine if a Client has a specific Product
* #param $clientId
* #param $productId
* #return bool
*/
public function clientHasProduct($clientId, $productId)
{
return ! is_null(
DB::table('client_product')
->where('client_id', $clientId)
->where('product_id', $productId)
->first()
);
}
You could put this in you User/Client model or you could have it in a ClientRepository and use that wherever you need it.
if ($this->clientRepository->clientHasProduct($clientId, $productId)
{
return 'Awesome';
}
But if you already have defined the belongsToMany relationship on a Client Eloquent model, you could do this, inside your Client model, instead:
return ! is_null(
$this->products()
->where('product_id', $productId)
->first()
);
#nielsiano's methods will work, but they will query DB for every user/product pair, which is a waste in my opinion.
If you don't want to load all the related models' data, then this is what I would do for a single user:
// User model
protected $productIds = null;
public function getProductsIdsAttribute()
{
if (is_null($this->productsIds) $this->loadProductsIds();
return $this->productsIds;
}
public function loadProductsIds()
{
$this->productsIds = DB::table($this->products()->getTable())
->where($this->products()->getForeignKey(), $this->getKey())
->lists($this->products()->getOtherKey());
return $this;
}
public function hasProduct($id)
{
return in_array($id, $this->productsIds);
}
Then you can simply do this:
$user = User::first();
$user->hasProduct($someId); // true / false
// or
Auth::user()->hasProduct($someId);
Only 1 query is executed, then you work with the array.
The easiest way would be using contains like #alexrussell suggested.
I think this is a matter of preference, so unless your app is quite big and requires a lot of optimization, you can choose what you find easier to work with.
Hello all) My solution for this problem: i created a own class, extended from Eloquent, and extend all my models from it. In this class i written this simple function:
function have($relation_name, $id) {
return (bool) $this->$relation_name()->where('id','=',$id)->count();
}
For make a check existing relation you must write something like:
if ($user->have('subscribes', 15)) {
// do some things
}
This way generates only a SELECT count(...) query without receiving real data from tables.
To check the existence of a relationship between 2 models, all we need is a single query against the pivot table without any joins.
You can achieve it using the built-in newPivotStatementForId method:
$exists = $client->products()->newPivotStatementForId($product->id)->exists();
use trait:
trait hasPivotTrait
{
public function hasPivot($relation, $model)
{
return (bool) $this->{$relation}()->wherePivot($model->getForeignKey(), $model->{$model->getKeyName()})->count();
}
}
.
if ($user->hasPivot('tags', $tag)){
// do some things...
}
This has time but maybe I can help someone
if($client->products()->find($product->id)){
exists!!
}
It should be noted that you must have the product and customer model, I hope it helps,

Advanced Filtering of Associated Entity Collection in Symfony

If I have an associated entity which is a collection, what options do you have when fetching?
e.g. Lets say I have a $view entity with this definition inside it:
/**
* #ORM\OneToMany(targetEntity="\Gutensite\CmsBundle\Entity\View\ViewVersion", mappedBy="entity")
* #ORM\OrderBy({"timeMod" = "DESC"})
*/
protected $versions;
public function getVersions() {
return $this->versions;
}
And I want to get the all the versions associated with the entity like this:
$view->getVersions();
This will return a collection. Great. But is it possible to take that collection and filter it by criteria, e.g. newer than a certain date? Or order it by some (other) criteria?
Or at this point are you just expected to do a query on the repository:
$versions = $em->getRepository("GutensiteCmsBundle:View\ViewVersion")->findBy(array(
array(
'viewId', $view->getId(),
'timeMod', time()-3600
)
// order by
array('timeMod', 'DESC')
));
There is a surprisingly unknown feature in recent versions of Doctrine, which makes these sort of queries much easier.
It doesn't seem to have a name, but you can read about it in the Doctrine docs at 9.8 Filtering Collections.
Collections have a filtering API that allows to slice parts of data from a collection. If the collection has not been loaded from the database yet, the filtering API can work on the SQL level to make optimized access to large collections.
In your case you could write a method like this on your View entity.
use Doctrine\Common\Collections\Criteria;
class View {
// ...
public function getVersionsNewerThan(\DateTime $time) {
$newerCriteria = Criteria::create()
->where(Criteria::expr()->gt("timeMod", $time));
return $this->getVersions()->matching($newerCriteria);
}
}
This will do one of two things:
If the collection is hydrated, it will use PHP to filter the existing collection.
If the collection is not hydrated, it will fetch a partial collection from the database using SQL constraints.
Which is really great, because hooking up repository methods to your views is usually messy and prone to break.
I also like #igor-pantovic's answer, although I've seen the method cause some funny bugs.
I would personally avoid using order by on annotation directly. Yes, you are supposed to do a query, just as you would if you were using raw SQL without Doctrine at all.
However, I wouldn't do it at that point but even before. In your specific case I would create an ViewRepository class:
class ViewRepository extends EntityRepository
{
public function findWithVersionsNewerThan($id, \DateTime $time)
{
return $this->createQueryBuilder('view')
->addSelect('version')
->join('view.versions', 'version')
->where('view.id = :id')
->andWhere('version.timeMod > :time')
->setParameter('time', $time)
->setParameter('id', $id)
->getQuery()
->getOneOrNullResult();
}
}
Now you can do:
$yourDateTime = // Calculate it here ... ;
$view = $em->getRepository("GutensiteCmsBundle:View\ViewVersion")->findWithVersionsNewerThan($yourDateTime);
$versions = $view->getVersions(); // Will only contain versions newer than datetime provided
I'm writing code from the top of my head here directly so sorry if some syntax or method naming error sneaked in.

Categories