Accessing involved models in MorphPivot class on boot methods - laravel

I have morph many-to-many relation between Tag vs Video and Thread, I'm using a custom model to represent the intermediate table of my relationships, I created a model which extends MorphPivot, So my question is, How to get models on boot method ? For example:
use Illuminate\Database\Eloquent\Relations\MorphPivot;
class TagResource extends MorphPivot
{
protected static function boot()
{
parent::boot();
self::created(function ($model){
dd($model);
});
}
}
I want whenever there is a relationship between my models is created i get them with created event on boot method, But when i dd($model) in the method it just returns the table attributes of TagResource and not the Tag or Video model, Is there a way to get involved models instances ?

Related

Target [Illuminate\Database\Eloquent\Model] is not instantiable while building

Peeps, I'm lost. Tried everything and after 5 hours of searching through the 10th page of Google hits, I give up. Maybe I just dont know how to ask Google the correct keywords..
I have this scenario: In lumen app, lets call it X, I have require custom packages CRUD and Storage, Storage is using functionality of CRUD.
StorageService has:
use Crud\Services\BaseService;
class StorageService extends BaseService{}
And Crud\BaseService has constructor, that uses Model:
use Illuminate\Database\Eloquent\Model;
class BaseService
{
protected $model;
public function __construct(Model $model)
{
$this->model = $model;
}
}
When I try to do anything with my app X, I get error:
Target [Illuminate\Database\Eloquent\Model] is not instantiable while building [Lumee\Storage\Services\StorageService]
I cannot get my head around how to get to proper class of Model, since I saw, that Model is abstract class.
Also, I'm using this CRUD package successfully in another App, only difference is, there CRUD is used directly in app, not via some other package. I'm confused, why there is working without any additional bindings and service registering..
EDIT: Added some binding into StorageServiceProvider (boot and register methods):
$this->app->bind(BaseService::class, function(){
return new BaseService(new Model());
});
And registered StorageServiceProvider in my boostrap/app.php:
$app->register(Storage\Providers\StorageServiceProvider::class);
Thing still returns same error. I tried with binding in CrudServiceProvider, nope.
you can't get object from abstract class (Model class) to solve this try this :
use Illuminate\Database\Eloquent\Model;
class BaseService
{
protected $model;
}
suppose your model is (Storage) :
use Crud\Services\BaseService;
class StorageService extends BaseService{
public function __construct(Storage $model)
{
$this->model = $model;
}
}

How to addMedia to another database using laravel-medialibrary

so i am trying to override laravel-medialibrary so that when i run
$modelA
->addMedia($attachment)
->withCustomProperties(['model_id' => $modelA->id])
->toMediaCollection('attachments_a');
, it adds a record into another specified database eg: database1_mysql
However, it is now currently adding into my current database(database2) instead..
I have created a Media model which has a connection already to another database
namespace App\Domains\Test\Models\Media;
class Media extends Model
{
protected $connection = 'database1_mysql';
}
And the model im relating the media to, i also have overriden the relationship to refer to the media that is connected to another table.
use App\Domains\Test\Models\Media;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
class ModelA extends Model implements HasMedia
{
use InteractsWithMedia;
public function media(): MorphMany
{
return $this->morphMany(Media::class, 'model');
}
}
found a way.
https://spatie.be/docs/laravel-medialibrary/v8/advanced-usage/using-your-own-model#breadcrumb
just swap the model in config

Laravel responsibility in the classes

I have a project on Laravel and need to do refactoring. I've read about Service provider and Dependency injection and have some questions.
This is a short structure: user model, event model, favorite user model and etc. Also, there are controllers for all models. Every event has a creator and client (user relationship). In every class, I am injecting appropriate service: User Service, Event service, Favorite user service and etc.
Let's consider the example - I want to delete the user:
class UserController extends Controller
{
/**
* #var UserService $userService
*/
protected $userService;
/**
* UserController constructor.
* #param UserService $userService
*/
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
protected function delete(int $id)
{
$user = User::find($id);
if ($user) {
$this->userService->setUser($user);
$this->userService->delete();
}
}
Inside User service, I am processing user deleting - update the appropriate field. Also, I need to cancel all user events and delete favorite users.
My question is where should I do it? Should I inject event and favorite user service in UserController or in UserService? Or maybe there is a better way to do this action. Thx in advance
Seems like you have many actions depending on deleting user, so I would consider using Events and inside each listener handle the specifics of it.
class UserController extends Controller
{
/**
* #var UserService $userService
*/
protected $userService;
/**
* UserController constructor.
* #param UserService $userService
*/
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
protected function delete(int $id)
{
if(!$this->userService->delete($id)) {
// return some error;
}
event(new UserWasRemoved($id));
// return success response
}
class DeleteUserService {
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function delete($id){
return $this->user->delete($id);
}
}
// app/Providers/EventServiceProvider
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
UserWasRemoved::class => [
CancelUserEvents::class,
RemoveUserFavorites::class,
// etc...
],
];
}
if deleting a user is much code, I will create DeleteUserService class which will contain all the code needed to delete a user and the effects of the delete.
class DeleteUserService {
public function __construct(int $userId)
{
$this->userId = $userId;
}
public function delete(){
$this->deleteUser();
$this->updateAppropriateFields(); // of course the name should be clearer
$this->deleteEvents();
$this->deleteFavoriteUser();
...
}
private function deleteUser(){...}
private function updateAppropriateFields(){...}
private function deleteEvents(){...}
private function deleteFavoriteUser(){...}
...
}
and in your controller either you inject the service or instantiate a new instance in the controller method
class UserController extends Controller
{
...
public function delete(int $id)
{
$user = User::findOrFail($id);
$deleteService = new DeleteUserService($user->id);
$deleteService->delete();
}
}
it's always a good idea to break your large function into one or more classes.
I suggest you abandon your approach to using services like this. Everything that you implement with services has already been implemented in Laravel, only even easier. You are now implementing more cumbersome logic on top of a simple, ready-made one.
For each object of your subject area (user, event, favorite user) add model class. Add in them the information of tables, the data from which belong to them - unless of course you use relational storage Eloquent Model Conventions. Here I have a question for you - does the favorite user entity need a separate class? If the User and the FavoriteUser have the same characteristics (that is, class members in the implementation), then there is no need to distribute them into different classes, and it is enough to add an additional isFavourite() (bool) attribute - in the class and in the table.
Implement the necessary methods in the controllers for each of your model classes as described in the documentation Defining Controllers. Depending on the type of the client part, the return of the response can be either JSON for the RESTful API, or a blade template with the transmitted data Views. Here, in the controller, you should implement a method to delete the model.
If you do not want the logic to be similar, that is, get rid of the similar methods all(), get(), post(), put(), delete() and others for UserController, for EventController, ... (with the exception of model classes - which will be different), then I advise you use the following architectural trick (this is optional, of course). Develop a universal layer - a class of a universal model, a class of a universal controller, a class of a universal model repository (if you use it in development). And in the controller, describe the common logic for all model classes, all(), get(), post(), put(), delete(). And then inherit each concrete class of the model from the universal, each concrete class of the controller from the universal - and so on. But!
In a concrete class of the model, it is necessary, for example, in an array, to list the attributes of the relational storage table, where you get the data from; it is also necessary to specify the name of the class in the variable - so that the controller can understand which class it should work with.
And in the controller in any way pass data about the model class - for example, using DependencyInjection Dependency Injection & Controllers.
With this approach, the classes of concrete controllers become thin, and the increase in code in them occurs only due to the redefinition of universal methods or the implementation of custom ones.
The advantage of this approach is also that there is no need to add routes of a similar structure. For example, a universal route will suffice for you
Route::get('{entity}/{id}', function ($entity, $id) {
$module = ucfirst($entity);
Route::get("{$entity}/{$id}", "{$module}Controller#get");
});
instead of many of the same type
Route::get('user/{id}', 'UserController#get');
Route::get('event/{id}', 'EventController#get');
and the like.

Issue in creating REST Service using laravel

We are trying to develop an Android app that required a REST API to show data from web server.
We tried to use Laravel resource to create REST service like below:
Route::resource('list', 'ListController');
namespace App\Http\Controllers;
use mymodel
class ListController extends Controller
{
public function getShow($id)
{
$Jsondata=list($id);
return $JsonData;
}
}
But it's not working as expected need some token key or some other authentication and authorization need to know how to set.
We should utilize the Repository / Gateway design pattern:
For example, when dealing with the User model, first create a User Repository. The only responsibility of the user repository is to communicate with the database (performing CRUD operations). This User Repository extends a common base repository and implements an interface containing all methods we require:
class EloquentUserRepository extends BaseRepository implements UserRepository
{
public function __construct(User $user) {
$this->user = $user;
}
public function all() {
return $this->user->all();
}
public function get($id){}
public function create(array $data){}
public function update(array $data){}
public function delete($id){}
// Any other methods we need go here (getRecent, deleteWhere, etc) }
Then, create a service provider, which binds your user repository interface to your eloquent user repository. Whenever you require the user repository (by resolving it through the IoC container or injecting the dependency in the constructor), Laravel automatically gives you an instance of the Eloquent user repository you just created. This is so that, if you change ORMs to something other than eloquent, you can simply change this service provider and no other changes to your codebase are required:
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider {
public function register() {
$this->app->bind(
'lib\Repositories\UserRepository', // Assuming you used these
'lib\Repositories\EloquentUserRepository' // namespaces
);
}}
Next, create a User Gateway, who's purpose is to talk to any number of repositories and perform any business logic of your application:
use lib\Repositories\UserRepository;
class UserGateway {
protected $userRepository;
public function __construct(UserRepository $userRepository) {
$this->userRepository = $userRepository;
}
public function createUser(array $input)
{
// perform any sort of validation first
return $this->userRepository->create($input);
}}
Finally, create our User web controller. This controller talks to our User Gateway:
class UserController extends BaseController
{
public function __construct(UserGatway $userGateway)
{
$this->userGateway = $userGateway;
}
public function create()
{
$user = $this->userGateway->createUser(Input::all());
}}
By structuring the design of your application in this way, you get several benefits: you achieve a very clear separation of concerns, since your application will be adhering to the Single Responsibility Principle (by separating your business logic from your database logic) . This enables you to perform unit and integration testing in a much easier manner, makes your controllers as slim as possible, as well as allowing you to easily swap out Eloquent for any other database if you desire in the future.
For example, if changing from Eloquent to Mongo, the only things you need to change are the service provider binding as well as creating a MongoUserRepository which implements the UserRepository interface. This is because the repository is the only thing talking to your database - it has no knowledge of anything else. Therefore, the new MongoUserRepository might look something like:
class MongoUserRepository extends BaseRepository implements UserRepository
{
public function __construct(MongoUser $user) {
$this->user = $user;
}
public function all() {
// Retrieve all users from the mongo db
}}
And the service provider will now bind the UserRepository interface to the new MongoUserRepository:
$this->app->bind(
'lib\Repositories\UserRepository',
'lib\Repositories\MongoUserRepository'
);
Throughout all your gateways you have been referencing the UserRepository, so by making this change you're essentially telling Laravel to use the new MongoUserRepository instead of the older Eloquent one. No other changes are required.
Number one is that what is not working? if you mean that the response is not showing then its because your function actually doesn't even seem to be going any where, and number two are you new to Laravel?
Okay if this is what you have:
namespace App\Http\Controllers;
use mymodel;
class ListController extends Controller
{
public function getShow($id)
{
$Jsondata=list($id); return $JsonData;
}
}
Then I can be safe to say a lot of things is wrong.
To my understanding using Route::resource('list', 'ListController'); in your routes file creates show, edit, update and destroy paths, and also expects to see these functions too (I have not proven this to the core though) but thats my understanding.
so you can simply start laravel life by doing having your routes with
Route::get('list', 'ListController#show')
Then change your ListController to something like this:
namespace App\Http\Controllers;
use mymodel;
class ListController extends Controller
{
public function show($id)
{
$jsonData= ['my' => 'json data'];
return $jsonData;
}
}
If this works for you, then cool else so many thing might already be wrong with your setup.
Note: you might need to take your time to learn to use laravel from the documentation page. If API is your interest then try DIngo api. Also try to learn to use markup :)
Hope this helps :)

Extending Eloquent Models in Laravel (use different tables)

I’m building a Laravel application that involves tracking different types of leads. For example, there are Refinance leads and Purchase leads.
Since the leads share a lot of information and functionality, but not all, my thinking was to create a Lead class, which extends Laravel’s Model class, and then a RefinanceLead class, which extends the Lead class.
So I’d have:
class Lead extends Model
{
// shared lead stuff
}
class RefinanceLead extends Lead
{
// stuff specific to refinance leads
}
My questions are:
Does this strategy make sense?
If it does, how is Eloquent going to handle the data? Will I have a leads table and a refinance_leads table?
Will a new instance of the RefinanceLead class utilize anything in the leads table?
I’ve had trouble answering this question via the documentation, but if I missed where this is explained, please let me know. Thanks.
1. Yes, it makes perfect sense to have all the common functionality in a parent model.
2. Basically each Eloquent model will handle the data from its own table defined in the protected $table variable. You can override the parent variable to set a separate table for all the different child models. Laravel Table Names
For example if you use the getId() method on a RefinanceLead instance it will return the id from refinance_lead table. If you use it on a PurchadeLead instance it will retirn the id from purchade_table
class Lead extends Model
{
public function getId() {
return $this->id;
}
}
class RefinanceLead extends Lead
{
protected $table = 'refinance_leads';
}
class PurchaseLead extends Lead
{
protected $table = 'purchase_leads';
}
3. I don't know what are your exact needs, but in general I'll suggest making the Lead class abstract and so you don't associate a table for it. Use it only to separate common functionality, relations, etc...
Of course as it was suggested in the comments, implementing an interface is always a good idea.
abstract class Lead extends Model implements LeadContract
{
// class body
}

Resources