Is it possible to create a query first and then assign it to a select?
For Example
$queryWithoutTarget = new Builder();
$queryWithoutTarget->where('x','y')->whereNull('z');
$workersQuery = Worker::select();
// I know this is Totally wrong, because queryWithoutTarget is not a parameter of $workersQuery
$workersQuery = $workersQuery->queryWithoutTarget;
I'm trying to encapsulate the query in functions and then execute them to any model or table
No, first you use $queryWithoutTarget as a variable (at the first and third row), then you use it as a function (at the 8th row ({...}->queryWithoutTarget)
Edit: If you override the Builder class then it may be possible. Open has maybe endless possibilities. But it's up to you to make overrides instead of additional lines in the controller
Use a scope. Scope is just a convenience method you can add to your model which encapsulates the syntax used to execute a query
Create your scope trait
namespace App\Models\Worker\Traits;
/**
* Class WorkerScope.
*/
trait WorkerScope
{
/**
* #param $query
*
* #return mixed
*/
public function scopeActive($query)
{
return $query->where('x','y')->whereNull('z');
}
}
Implement your scope on model
use App\Models\Worker\Traits\WorkerScope;
/**
* Class Worker.
*/
class Worker extends Model
{
use WorkerScope;
...
}
With the scope defined, you can execute it like
$workersQuery = Worker::active()->get();
Related
So I try to have something like a global scope on my model so that I don't have to go and think about some parameters every single time.
The thing is I currently have the following Scopes-class;
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class NeedsPaymentScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* #param Builder $builder
* #param Model $model
* #return void
*/
public function apply(Builder $builder, Model $model)
{
if ($model->needs_payment) {
$builder->where('is_paid', '=', true);
}
}
}
And I have it included in my model like so:
protected static function boot()
{
parent::boot();
static::addGlobalScope(new NeedsPaymentScope());
}
Now, since in the Scope class, the variable $model is like always empty, I can't execute the if-check (which needs to be done!) How can I have this logic implemented to every get(), find(); findOrFail(), ... method?
You can NEVER access $model->needs_payment because model is not prepared yet, in other words you are in the middle of writing the query statement you can't access the model that it's query has not yet been completed.
try view composer for globle scope of variable.
with the help of AppServiceProvider you can define a globle scope of variable.
public function boot(){
View::share('variable_name','your_variable_logic_code');
}
if you have so many lines of code or logic you can use callback function tooo.
now you can access variable everywhere without passing in blade.
no comapct no with(method) without return to a blade you can access in whole app.
i think its helpfull for you.
All of my tables have a column called isTest. What I want is to be able to set a switch so that my code will either include all records in my queries or [more importantly] exclude all records where isTest is true.
I imagine the code will work similarly to Soft Deletes and include sql code similar to: AND (isTest != TRUE) to SQL generated by Eloquent and the Query Builder.
I am not really familiar with Eloquent events, but I have found this question which might be the right place to start, but I am hoping for guidance before I start down that path. Also, that has no info about Query Builder. If someone has done something similar I would love some advice.
You are looking for Global scopes, you can add a custom scope which will check the isTest value.
<?php
// Your custom scope
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class IsTestScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* #param \Illuminate\Database\Eloquent\Builder $builder
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->where('isTest', true);
}
}
// Your model
namespace App;
use App\Scopes\IsTestScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
// Check for certain criteria, like environment
if (App::environment('local')) {
// The environment is local
static::addGlobalScope(new IsTestScope);
}
}
}
When you have a lot of models, you want to make a trait of this code so you dont have to duplicate it all the time. Like how SoftDeletes work.
See docs for more info https://laravel.com/docs/5.8/eloquent#global-scopes
To create a scope in Laravel, we define manually a public method that matches the name that we want to use when building queries:
/**
* Scope of Microsoft organization.
*
* #param Builder $query
* #return Builder
*/
public function scopeMicrosoft($query)
{
return $query->where('slug', 'microsoft');
}
Usage:
Organization::microsoft()->first();
Now imagine that we have >50 organizations and want to automatically have a scope for every model based on slug attribute. There's a way to create scopes massively?
From docs:
Dynamic Scopes
Sometimes you may wish to define a scope that accepts parameters. To get started, just add your additional parameters to your scope. Scope parameters should be defined after the $query parameter:
namespace App;
use Illuminate\Database\Eloquent\Model;
class SomeModel extends Model
{
/**
* Scope a query to only include users of a given type.
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #param mixed $type
* #return \Illuminate\Database\Eloquent\Builder
*/
public function scopeOrganization($query, $org)
{
return $query->where('slug', $org);
}
}
Now, you may pass the parameters when calling the scope:
$users = App\SomeModel::organization('Microsoft')->get();
You can use a global scope if you're going to use this scope in all models.
Or add this local scope into a trait and use it in multiple models.
Use this package for dynamic global scope make,
https://github.com/limewell/laravel-make-extender
php artisan make:scope ActiveScope
php artisan make:scope UserScope
php artisan make:scope AgeScope etc...
Suppose I have the following PHP function:
/**
* #param string $className
* #param array $parameters
* #return mixed
*/
function getFirstObject($className, $parameters) {
// This uses a Doctrine DQl builder, but it could easily replaced
// by something else. The point is, that this function can return
// instances of many different classes, that do not necessarily
// have common signatures.
$builder = createQueryBuilder()
->select('obj')
->from($className, 'obj');
addParamClausesToBuilder($builder, $parameters, 'obj');
$objects = $builder
->getQuery()
->getResult();
return empty($objects) ? null : array_pop($objects);
}
Basically, the function always returns either an instance of the class specified with the $className parameter or null, if something went wrong. The only catch is, that I do not know the full list of classes this function can return. (at compile time)
Is it possible to get type hinting for the return type of this kind of function?
In Java, I would simply use generics to imply the return type:
static <T> T getOneObject(Class<? extends T> clazz, ParameterStorage parameters) {
...
}
I am aware of the manual type hinting, like
/** #var Foo $foo */
$foo = getOneObject('Foo', $params);
but I would like to have a solution that does not require this boilerplate line.
To elaborate: I am trying to write a wrapper around Doctrine, so that I can easily get the model entities that I want, while encapsulating all the specific usage of the ORM system. I am using PhpStorm.
** edited function to reflect my intended usage. I originally wanted to keep it clean of any specific use case to not bloat the question. Also note, that the actual wrapper is more complex, since I also incorporate model-specific implicit object relations and joins ect.
I use phpdoc #method for this purpose. For example, I create AbstractRepository class which is extend by other Repository classes. Suppose we have AbstractRepository::process(array $results) method whose return type changes according to the class that extends it.
So in sub class:
/**
* #method Car[] process(array $results)
*/
class CarRepo extends AbstractRepository {
//implementation of process() is in the parent class
}
Update 1:
You could also use phpstan/phpstan library. Which is used for static code analyses and you can use it to define generic return types:
/**
* #template T
* #param class-string<T> $className
* #param int $id
* #return T|null
*/
function findEntity(string $className, int $id)
{
// ...
}
This can now be achieved with the IntellJ (IDEA/phpStorm/webStorm) plugin DynamicReturnTypePlugin:
https://github.com/pbyrne84/DynamicReturnTypePlugin
If you use PHPStorm or VSCode (with the extension PHP Intelephense by Ben Mewburn) there is an implementation named metadata where you could specify your own type-hinting based on your code doing the magic inside. So the following should work (as it did on VSCode 1.71.2)
<?php
namespace PHPSTORM_META {
override(\getFirstObject(0), map(['' => '$0']));
}
I have an Eloquent model. Whenever it is retrieved from the database I would like to check whether a condition is fulfilled and set a model attribute if this is the case.
EDIT: I initially thought that the restoring event would be the right place to put the relevant logic, but as Tyler Crompton points out below, restoring is fired before a soft-deleted record is restored.
You have two valid options:
You can subclass \Illuminate\Datebase\Eloquent\Model to add such an event.
You can modify your copy of \Illuminate\Datebase\Eloquent\Model to add this (and possibly send an (unsolicited) pull request to Laravel on GitHub). According to Issue 1685, it looks as though they do not want it.
If I were you, I'd go with the first option and this is how I'd do it:
<?php namespace \Illuminate\Database\Eloquent;
abstract class LoadingModel extends Model {
/**
* Register a loaded model event with the dispatcher.
*
* #param \Closure|string $callback
* #return void
*/
public static function loaded($callback)
{
static::registerModelEvent('loaded', $callback);
}
/**
* Get the observable event names.
*
* #return array
*/
public function getObservableEvents()
{
return array_merge(parent::getObservableEvents(), array('loaded'));
}
/**
* Create a new model instance that is existing.
*
* #param array $attributes
* #return \Illuminate\Database\Eloquent\Model|static
*/
public function newFromBuilder($attributes = array())
{
$instance = parent::newFromBuilder($attributes);
$instance->fireModelEvent('loaded', false);
return $instance;
}
}
Just make sure the models in question subclass from LoadingModule. I have confirmed this to work as I found a great use case for it. Older versions of PHP returned MySQL values as strings. Normally, PHP will silently cast these to their respective numeric types in numeric operations. However, converting to JSON is not considered a numeric operation. The JSON values are represented as strings. This can cause problems for clients of my API. So I added a loaded event to my models to convert values to the correct type.
You could do this on the way in, or the way out. It seems like you wanted it stored in the database, so you could use mutators.
class Foo extends Eloquent {
public function setBAttribute($value)
{
if ($this->attributes['a'] == $this->attributes['b']) {
$this->attributes['b'] = 1;
}
}
}
When ever B is set, it will check against A, and store 1 in B.
Side note: Note the B between set and attribute
I think this is the best option you can use.
The retrieved event will fire when an existing model is retrieved from the database. for example, if you have a User model in your application you must define code like below in User Model.
self::retrieved(function (self $model){
//all your code here
});