Where to place this Eloquent query logic in Laravel 9? - eloquent

Let's say I have a model Book with a field published that is either true or false and I often need to retrieve books that have been published in the current year. I can create following Eloquent query:
Book::where(['published', true], ['publish_date', date('Y')])->get();
Where should I place this code? In my controller or my model? And how would I call it?
Book->getPublished()
Book::getPublished()

Typically you'd use Query Scopes for handling this commonly accessed logic. Add the following method to your Book model:
class Book extends Model
{
public function scopePublished($query)
{
return $query
->where('published', true)
->where('publish_date', date('Y));
}
}
You can then use this scope in your code via Book::published()->get().
The benefit of declaring the scope, but not including the ->get() is that you can chain multiple methods when required. For example:
$books = Book::published()->order_by('publish_date')->limit(25)->get();

Related

Laravel Eloquent Models __construct method to call relations

I want to have my model automatically call its relations when instantiated. As of now my model looks like this:
class AdminLog extends Model{
public function __construct(){
$this->belongsTo('App\User', 'admin_id');
}
}
but when i try to do dd(AdminLog::get()->first());, it doesnt show any relations.
Edit#1: tried adding parent::__construct(); inside the model's __construct method but it didn't work.
belongsTo() defines a relationship, it doesn't load it.
First you need to define the relationship, then you can load it at any point using the load method.
class AdminLog extends Model {
public function user() {
return $this->belongsTo(\App\User::class, 'admin_id');
}
}
$log = AdminLog::first();
$log->load('user');
It is possible to load inside the constructor, but I would highly recommend against that. If you have 20 AdminLog objects then it will query the database 20 times, once for each object. That's inefficient.
What you should do instead is use eager loading. This will query the users table just once for all 20 admin logs. There are many ways to do this, here is an example:
$logs = AdminLog::take(20)
->with('user')
->get();
dd($logs->toArray());

Mass CRUD REST edit/update controller

I am trying to create a RESTful CRUD controller with a little but significant difference that might be in conflict with REST idea but anyway:
I am trying to mass edit items like so /photos/{photo}/edit where item id parameters are like /photos/0&2&7/edit
What is the proper way to establish that in Laravel 5.3?
Is there a way to use some method injections or at least to receive a collection of parameters in the controller method ?
public function edit($id) {
//.......
}
Appreciate your kind help, BR
Using Eloquent you can do whereIn, so you just need to explode the photo parameter so that all the ids are in an array:
public function edit($ids) {
$photo_ids = explode('&', $ids);
$images = Image::whereIn('id', $photo_ids)->get();
}
You can switch out statically accessing the Image model like I did in this example, you can just method inject or dependency inject the image model, let me know if you'd like assistance with dependency/method injection.
Hey i guess you are trying Model binding so you have to use like this
public function edit(Photo $photo) {
//.......
}
Your route should like this
Route::model('photos','App\Photo');
Route::resource('photos','PhotoController');
or you can try this way
your route and function like this
Route::resource('photos','PhotoController');
public function edit($id) {
$photo = Photo::findorFail($id);
}

REST Api with QueryParamAuth authenticator - Yii2

I'm trying to create rest api for my application to get the data in my android app. This is my controller
<?php
namespace api\modules\v1\controllers;
use yii\rest\ActiveController;
use yii\filters\auth\QueryParamAuth;
/**
* Tk103 Controller API
*/
class Tk103Controller extends ActiveController
{
public $modelClass = 'api\modules\v1\models\Tk103CurrentLocation';
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => QueryParamAuth::className(),
];
return $behaviors;
}
}
I added access_token column in my user table, implemented findIdentityByAccessToken() in User Model and calling this URL
http://localhost:7872/api/v1/tk103s?access-token=abcd
This is working great and returning data if and only if access_token matches with any single user in the table.
I checked QueryParamAuth class and found that QueryParamAuth::authenticate() returns $identity after successful authentication.
Currently this url is returning whole data of my table.
What I want is(after authentication):
Get user id/username of the requester.
Based on that id/username, the data related to him as per relations of tables in db. (currently whole rows are being returned but I want only few that are matching with the current requester/user)
I tried but didn't getting any clue to catch returned $identity of user after authentication.
And I know it is possible too to make this work. Help me out folks to create magic.
Get user id/username of the requester.
That user instance you did return within the findIdentityByAccessToken method should be accessible any where inside your app within Yii::$app->user->identity. And should hold all the attributes retreived from DB. here is a quick example of using it to check access within the checkAccess method of the ActiveController class:
public function checkAccess($action, $model = null, $params = [])
{
// only an image owner can request the related 'delete' or 'update' actions
if ($action === 'update' or $action === 'delete') {
if ($model->user_id !== \Yii::$app->user->identity->id)
throw new \yii\web\ForbiddenHttpException('You can only '.$action.' images that you\'ve added.');
}
}
Note that the checkAccess is by default an empty method that is manually called inside all the built-in actions in ActiveController. the Idea is to pass the action ID and the model instance to it just after retrieving it from DB and before modifying it so we can do extra checks. If you just need to perform checks by actions ID then yii\filters\AccessControl may be enough but inside checkAccess you are expecting to also get the model instance itself so it is important to note that when building your own actions or overriding existing onces. be sure to manually invoke it the same way it is done in UpdateAction.php or DeleteAction.php.
whole rows are being returned but I want only few .. matching with .. current requester/user
It depends on how your data is structured. You can override ActiveController's actions to filter results before outputting them, it can be handled in the related SearchModel class if you are using one or it can be handled in model. A quick tip may be by simply overriding the find method inside your model:
public static function find()
{
return parent::find()->where(['user_id' => Yii::$app->user->getId()]); // or Yii::$app->user->identity->id
}
Note that this works only when using ActiveRecord. Which means when using this:
$images = Image::find()->all();
The find method we just overriden will be filtered by default by always including that where condition before generating the DB query. Also note the default built-in actions in ActiveController are using ActiveRecords but if you are using actions where you are constructing the SQL queries using the Query Builder then you should manually do the filtering.
The same can be done if using ActiveQuery (maybe better explained here) by doing this:
public static function find()
{
$query = new \app\models\Image(get_called_class());
return $query->andWhere(['user_id' => Yii::$app->user->getId()]);
}

Eclipselink JPA MappingSelectionCriteria customization

According to EclipseLink/Examples/JPA/MappingSelectionCriteria I can make some filtering on OneToOne or OneToMany relationships. To do that I have to implement DescriptorCustomizer.
My question is: Can I do some conditional filtering with this technique and how? I mean, in the example of mentioned link we can write something like this
public class ConfigureBsFilter implements DescriptorCustomizer {
public void customize(ClassDescriptor descriptor) throws Exception {
OneToManyMapping mapping = (OneToManyMapping) descriptor
.getMappingForAttributeName("bs");
ExpressionBuilder eb = new ExpressionBuilder(mapping
.getReferenceClass());
Expression fkExp = eb.getField("A_ID").equal(eb.getParameter("A_ID"));
Expression activeExp = eb.get("active").equal(true);
mapping.setSelectionCriteria(fkExp.and(activeExp));
}
}
But what if in the expression
Expression activeExp = eb.get("active").equal(true);
the "active" is not always true but have to be set at runtime by some parameter. Can I do that and how?
Looking at wiki.eclipse.org/Using_Advanced_Query_API_(ELUG) you could use a query redirector on the ForeignReferenceMapping#getSelectionQuery() so that your query redirector can dynamically clone the query and add filters as required. Passing parameters to the redirector will need to be creative though, such as storing them on the thread context or in the session's properties map.

Dependency Injection - use with Data Transfer Objects (DTOs)?

Consider the code below (which has been simplified). I have a service class that returns a list of specific DTO objects that each implement their own specific interface. In the actual code these are getting populated by iterating thru a Dataset as I'm working with legacy code.
Questions:
How do we create/use a DTO without newing them up or using the Service Locator anti-pattern? It doesn't make much sense to compose an empty DTO object in the Composition Root and inject it into the Service class via the constructor, because I'd actually be using the DTO as a temporary variable of sorts while populating a list.
In the code you can see an example of me newing up the DTO. But this doesn't feel much
better than if I made the DTOs not implement interfaces in the first place. So should they not implement interfaces then and thus, not use DI with DTOs?
public class Services : IServices
{
public IList<IDTO> GetDTOs()
{
...
List<IDTO> dtos = new List<IDTO>();
foreach (c in d)
{
DTO dto = new DTO();
dto.x = c.x;
dto.y = c.y;
dto.z = c.z;
dtos.Add(dto);
}
return dtos;
}
}
it doesn't make much sense to me to use any DI for DTOs. I would probably use the Factory Pattern to get DTOs for my model objects.
DTOs don't need their life cycle managed by the container; I would just new them. Dont over-engineer.
I don't think DTOs should implement interfaces, because they aren't likely to implement behavior that will change.
They also shouldn't be injected. Not all objects should be. I think this is an appropriate call to new: create the object, use it, let it go out of scope and be GC'd.
Have a look at AutoMapper. And I agree with #duffymo, I wouldn't use interfaces with DTO's. AutoMapper is a convention-based object to object mapper that will create and populate your DTO's for you. If nothing else it will save you a lot of typing. I've been through the exercise of writing conversion routines to/from DTO's with associated typos. I wish I had found AutoMapper a bit sooner. In the case of your example (where I've nominally made the "from" object of type Order):
public class Services : IServices
{
public IList<DTO> GetDTOs()
{
...
Mapper.CreateMap<Order, DTO>(); // move map creation to startup routine
var dtos = new List<DTO>();
foreach (c in d)
{
dtos.Add( Mapper.Map<Order, DTO>(c));
}
return dtos;
}
}
Or using LINQ
public class Services : IServices
{
public IList<DTO> GetDTOs()
{
...
Mapper.CreateMap<Order, DTO>(); // move map creation to startup routine
return d.Select(c => Mapper.Map<Order, DTO>(c)).ToList();
}
}