symfony 4 count method with doctrine-orm - php

I am a beginner in symfony 4, and I wanted to count the recording of an object, but it does not work.
this is my code
// Function count() in my repository class
public function count()
{
return $this->createQueryBuilder('d')
->select('count(d.codeMarche) as count')
->getQuery()
->getSingleScalarResult();
}
// in my controller class, i called my function count()
/**
* #Route("/", name="home")
*/
public function hom(MarcheDAORepository $repo){
$count = $repo->count();
return $this->render('index/home.html.twig', ['nbrDAO'=>$count]);
}
i need your help!!

The count method is already defined in the parent class so you can simply omit your implementation and use the default one (that return an int value as the doc suggest) or rename your method if you want to use a custom implementation, as example
public function countCodeMarche()
{
return $this->createQueryBuilder('d')
->select('count(d.codeMarche) as count')
->getQuery()
->getSingleScalarResult();
}
That will return a string and you should cast to int if you need a different type.

You can do that by the following code :
public function hom(MarcheDAORepository $repo){
$items = $repo->findAll();
$count=count($items);
return $this->render('index/home.html.twig', ['nbrDAO'=>$count]);
}

Related

Laravel eloquent query using local scope with conditional statement and arguments

I've written the following function in my Car model that does the following:
Gets the related reservations based on two dates(pickup/dropoff)
Checks if the amount of these reservations are equal or exceed the quantity of the car
Finally returns a boolean depending on the output
/**
* Custom Functions
*/
public function isAvailableFor($from, $to) {
$reservationsCount = $this->reservations->where('pickup_date', '>=', $from)->where('dropoff_date', '<=', $to)->count();
if($reservationsCount >= $this->quantity) {
return false;
}
return true;
}
The function is working as expected but I want to implement this in a more elegant way using local scopes so I can actually use it efficiently when querying the Car model in my controllers but I can't find the correct way to do it and my code becomes a complete mess.
For example I have the following scope that I am using by just typing Car::active()->get();
/**
* Scopes
*/
public function scopeActive($query)
{
return $query->where('status', 'active');
}
The main problem is the count() function that doesn't let me implement my function in a scope-way or at least I am not that experienced to come up with a solution.
Thanks in advance.
Update
As correctly pointed by OsDev since my function returns a boolean it can not be implemented directly in the scope function. I can alternatively do this in my scope function but I guess it is pretty much an overkill:
public function scopeAvailable($query, $from, $to) {
$excludedId = array();
$cars = Car::whereHas('reservations')->get();
foreach($cars as $car) {
if(!$car->isAvailableFor($from, $to)) {
array_push($excludedId, $car->id);
}
}
return $query->whereNotIn('id', $excludedId);
}
You have to return the $query instead the count result because that way you don't break the Query Builder chain
You can't combine scopes and Model functions because scopes are supposed to return the $query builder object and in that example, your function is returning a boolean.
You can do something like this
/**
* Scopes
*/
public function scopeIsAvailableFor($query,$from,$to)
{
return $query->where('pickup_date', '>=', $from)->where('dropoff_date', '<=', $to);
}
Then you can chain it and call count if you want
$count = Car::active()->isAvailableFor('2020-05-03','2020-05-06')->count();
Maybe you can wrap your new scope into your model method
public function isAvailableFor($from, $to) {
$reservationsCount = $this->reservations->isAvailableFor($from,$to)->count();
return !$reservationsCount >= $this->quantity;
}

How to add new method chaining for Laravel Eloquent?

I try to add new method for simplify and reusable code but I failed
User Model :
public function products()
{
return $this->hasMany('App\Product');
}
public function obsolate()
{
return $this->where('status', 'obsolate');
}
When I try to retrieve like auth()->user()->products()->obsolate() it gives some error like
BadMethodCallException: Call to undefined method Illuminate\Database\Eloquent\Relations\HasMany::obsolate()
But If I do like auth()->user()->products()->where('status', 'obsolate')->get() It works.
Please, correct me if I wrong...
UPDATE
Product Model :
public function user()
{
return $this->belongsTo('App\User');
}
public function scopeObsolate($query)
{
return $query->where('status', 'obsolate');
}
I do auth()->user()->products->obsolate()->get() and it works!
But, if I want to use constraint group to solve this problem it will return error
public function scopeObsolate(Builder $builder)
{
return $builder->where(function (Builder $query) {
return $query->where('status', 'obsolate');
});
}
Argument 1 passed to App\Product::App{closure}() must be an instance of App\Builder, instance of Illuminate\Database\Eloquent\Builder given
SOLVED
use Illuminate\Database\Eloquent\Builder
Because products() method return Illuminate\Database\Eloquent\Relations\HasMany object. When you applying where to it, laravel will set the constraints on the relation query(It means Product eloquent builder). So you need to defined the method in Product model.
And change the obsolate method to scope method:
// Product model:
public function scopeObsolate($query)
{
return $query->where('status', 'obsolate');
}
Update:
The second error occurs because the type-hint, laravel cannot find the Builder in model. You need to
use Illuminate\Database\Eloquent\Builder
I am not using Laravel, but:
Your ->where/->hasMany seems to return different (internal) object that does not contain your custom method.
To chain properly, return $this only:
class FooBar extends \Illuminate\Database\Eloquent
{
public function products()
{
$this->hasMany('App\Product');
return $this;
}
public function obsolate()
{
$this->where('status', 'obsolate');
return $this;
}
}

How to pass parameter in Laravel model relation

I made a category tree and I need to pass one parameter to relation, I can't pass them.
public function Child()
{
return $this->hasMany(Category::class, 'parent_id', 'id');
}
but I want to use variable to pass in relation look like this.
public function Child()
{
return $this->hasMany(Category::class, 'parent_id', 'id')->where(['owner_id' => $this->ownerId]);
}
then I try to use variable and receive nothing, but if I use hardcoded value then works well. Please help
$models = App\{YourMainModel}::with(['Child' => function ($query) use ($this) {
$query->where(['owner_id' => $this->ownerId]);
}])->get();
You will need to add a constructor to your Child model (which extends the class Model).
private ownerId;
public function __construct(int ownerId)
{
parent::__construct($attributes);
$this->ownerId = $ownerId;
}
Then you can access this throughout your class.
public function child()
{
return $this->hasMany(Category::class, 'parent_id', 'id')->where('owner_id', $this->ownerId);
}
You would do this if every time you wanted to instantiate your class Child, you would have to give it an owner:
$ownerId = 5;
$child = new Child($ownerId);
Alternatively, you could pass a parameter directly to that function from wherever you call it, like:
public function childWithOwner(int $ownerId)
{
return $this->hasMany(Category::class, 'parent_id', 'id')->where('owner_id', $ownerId);
}
And you would call it: $this->childWithOwner(4);
As a tip I would encourage you to start your function names with a lowercase letter.

How to read a DC2Type array with doctrine and symfony

I'm setting up an advanced search for a jobboard and I need to find resumes by contract, knowing that a resume can have multiple contracts.
I have a form where you can choose which type of contract you are looking for (It's a ChoiceType::class with multiple => true)
In my table, my colomn contract :
In my Entity Resume :
/**
* #ORM\Column(type="array", nullable=true)
*/
private $contract = [];
public function getContract(): ?array
{
return $this->contract;
}
public function setContract(?array $contract): self
{
$this->contract = $contract;
return $this;
}
In my repository :
public function findByContract(array $contract)
{
return $this->createQueryBuilder('r')
->andWhere('r.contract IN (:cons)')
->setParameter('cons', $contract)
->getQuery()
->getResult()
;
}
In my controller :
public function index(Request $request, ResumeRepository $resumeRepository)
{
$formSearch = $this->createForm(ResumeSearchFormType::class);
$formSearch->handleRequest($request);
if ($formSearch->isSubmitted() && $formSearch->isValid()) {
$data = $formSearch->getData();
$results = $resumeRepository->findByContract($data->getContract());
var_dump($results); die;
This var_dump() returns an empty array.
I don't know how I can do to find resume by contract
I suppose that you find the solution but maybe I can help someone else!
I had the same issue, so I used LIKE instead of IN, and I used the setParameter differently.
Like this :
return $this->createQueryBuilder('r')
->andWhere('r.contract LIKE (:cons)')
->setParameter('cons', '%' .$contract. '%')
->getQuery()
->getResult();
If you want more information about it, check this: What is DC2Type array datatype in mysql

Laravel 5.7: New Accessor works great but is not recognisable in Repository Class (unknown column) despite of adding $appends to Model

So I have a class Order extends Model.
I created an Accessor called requiresApproval that returns true or false.
public function getRequiresApprovalAttribute(): bool
{
if ($some_physical_column_from_db === 'does not matter') {
return true;
}
return false;
}
When I have my Order model and I call $order->requiresApproval I get my boolean value. Everything works great.
However I need this accessor to appear on the list of attributes because I want to use it in my repository class in where condition within query.
So based on the official documentation, I added:
protected $appends = [
'requires_approval',
];
but when I dd() my Order, this attribute is not on the list of attributes (while $appends property indicates the accessor is there).
Long story short:
When in my repository I call:
public function getOrdersEligibleToBeSendToOptions(): Collection
{
$columns = [
'*',
];
return $this->model
->where('status_uuid', '<>', OrderStatusesInterface::STATUS_COMPLETED)
->where('requiresApproval', '=', true) // this fails
// ->where('requires_approval', '=', true) // this fails as well
->get($columns);
}
I get:
What am I doing wrong? How can I use my accessor within repository class?
OK, this works, but the reason I don't like this solution is the fact that just half of the conditions are on the DB layer, the rest is by filtering what's already fetched.
If the query is going to return (let's say) thousand of records and filter returns just a few of them I personally see this as a huge waste of DB resource.
public function getOrdersEligibleToBeSendToOptions(): Collection
{
$columns = [
'*',
];
$results = $this->model
->where('status_uuid', '<>', OrderStatusesInterface::STATUS_COMPLETED)
->get($columns);
return $results->filter(function ($value, $key) {
return $value->requiresApproval === false;
});
}
Eloquent queries work on the database fields, but you can use your accessor after fetching a colletion from the database like this.
Here is some good article about this:
https://medium.com/#bvipul/laravel-accessors-and-mutators-learn-how-to-use-them-29a1e843ce85
return $this->model
->where('status_uuid', '<>', OrderStatusesInterface::STATUS_COMPLETED)
->get($columns)
->filter(function ($row) {
return $row->requires_approval === true;
});
The model virtual attributes cannot be used within queries. Perhaps a better approach would be to create a scope to enforce this constraint on a query:
class Order extends Model
{
public function scopeRequiresApproval($query)
{
return $query->where('some_column', '>', 100);
}
}
Then
return $this->model
->where('status_uuid', '<>', OrderStatusesInterface::STATUS_COMPLETED)
->requiresApproval()
->get($columns);

Categories