Override vendor model in laravel 6.X - laravel

I want to override the model inside of vendor. I tried bellow code. But not working.
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
//
}
public function boot()
{
$this->app->bind('VendorName\Models\User', 'App\Models\User');
}
}
Extending model is not an option, as i have to override all controller change model path and write all methods again, its not worth it.

Binding a class in the container like this isn't going to override direct references to the class you're trying to override if the object isn't being resolved using the service container.
So for example, something like $user = new \VendorName\Models\User; isn't going to be affected because it's simply not using the container.
I think the only sensible solution is to refactor your code so you're using a class that extends the base User class.

Related

Laravel Livewire error when I add a constructor to call a service class

I've got a piece of code I want to reuse. I've read this Laravel cleaner code article and this other Laravel Services Pattern article, where I have realized I can reuse code in several places of the application by using services classes.
In this case, I created a new MyService class, inside a new folder app/Services/MyService.
namespace App\Services;
class MyService
{
public function reuse_code($param){
return void;
}
}
The problem comes when I want to call the class through the constructor inside a Livewire class component, as follows:
<?php
namespace App\Http\Livewire;
use App\Services\MyService;
use Livewire\Component;
use Livewire\WithPagination;
class LivewireTable extends Component
{
use WithPagination;
private $myClassService;
public function __construct(MyService $myService)
{
$this->myClassService = $myService;
}
public function render()
{
$foo = $this->myClassService->reuse_code($param);
return view('my.view',compact('foo'));
}
}
The error displayed is the following:
Argument 1 passed to App\Http\Livewire\LivewireTable::__construct()
must be an instance of App\Services\MyService, string given
(However, If I use a trait, there are no problems. But I am afraid then my traits collide as previous experiences)
How do I fix it? What am I missing?
Livewire's boot method Runs on every request, immediately after the component is instantiated, but before any other lifecycle methods are called
Here's the solution worked for me.
Solved
Just like #IGP said, reading in the livewire docs it says:
In Livewire components, you use mount() instead of a class constructor
__construct() like you may be used to.
So, my working code is as follows:
<?php
namespace App\Http\Livewire;
use App\Services\MyService;
use Livewire\Component;
use Livewire\WithPagination;
class LivewireTable extends Component
{
use WithPagination;
private $myClassService;
public function mount(MyService $myService)
{
$this->myClassService = $myService;
}
public function render()
{
$foo = $this->myClassService->reuse_code($param);
return view('my.view',compact('foo'));
}
}

PHPUnit tests failing after adding a ViewServiceProvider for sharing data to views

I am relatively new to testing so please forgive me if this a stupid question.
All my tests are failing after I added a ViewServiceProvider to share data with the views.
The error is:
Illuminate\Database\QueryException : SQLSTATE[HY000]: General error: 1 no such table: policies (SQL: select "name", "slug" from "policies")
My tests are making use the the refresh database trait:
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
use RefreshDatabase;
...
}
Here is an example of the ViewServiceProvider:
namespace App\Providers;
use App\Models\Policy;
use Illuminate\Support\ServiceProvider;
class ViewServiceProvider extends ServiceProvider
{
public function register(): void{...}
public function boot(): void
{
view()->share('policies', Policy::all(['name', 'slug']));
}
}
Every works when browsing the site on front end. Am i missing something? Why are the tests failing?
I will appreciate suggestions on how to make the tests pass.
Edit
It makes total sense what #Donkarnash said but it's also confusing because according to the docs:
So, what if we need to register a view composer within our service provider? This should be done within the boot method. This method is called after all other service providers have been registered, meaning you have access to all other services that have been registered by the framework
See https://laravel.com/docs/8.x/providers#the-boot-method
I found the solution. Creating a view composer solved the problem.
namespace App\Http\View\Composers;
use App\Models\Policy;
use Illuminate\View\View;
class PolicyComposer
{
public function compose(View $view): void
{
$view->with('policies', Policy::all(['name', 'slug']));
}
}
Then I referenced the composer in the ViewServiceProvider
namespace App\Providers;
use App\Models\Policy;
use Illuminate\Support\ServiceProvider;
class ViewServiceProvider extends ServiceProvider
{
public function register(): void{...}
public function boot(): void
{
view()->composer('*', PolicyComposer::class);
}
}

How to use different method of a controller in laravel application

In my laravel application i should use some create and update method of some controller in another controller
According to my search is not a good thing to call a method from controller in another
I cant see the why don't call a controller method in another controller
I'm doing this way :
class Controller extends BaseController
{
protected $variable;
public function __construct()
{
$this->variable = "Hello";
}
}
and
class ClientController extends Controller
{
public function __construct()
{
parent::__construct();
}
}
The __constructor is a magic method of class. It calls when you trying to create instance of class. So there is no way to use constructor without creating an instance or extendeding from another class. If you have a common code in different classes there a best way to use traits. thats give you an opportunity to include your trait and use methods ,making your code beutiful , flexible , readable following principes DRY,KISS.
you can create a base class with constructor and extend other controller of it
or you can put your code in to Http\Controllers\controller.php ('main controllers constructor')
also you can use trait

How to override a Laravel package function

I'm using https://github.com/artesaos/seotools package for seo.
I am trying to override getCanonical function located at https://github.com/artesaos/seotools/blob/master/src/SEOTools/SEOMeta.php and make it's output as lowercase. Could you please guide me how can I do this?
You can try following :
Step 1:
Create a child class extending SEOMeta class and override the getCanonical function.
Class XyzSEOMeta extends SEOMeta {
public function getCanonical () {
// Write your logic here
}
}
Step 2:
Create the Service Provider for overridden class. First parameter of bind function must be same as facade accessor of SEOMeta Facade (check here). Register this facade in config/app.php after the service provider of seotools package. :
Class XyzSEOMetaServiceProvider extends ServiceProvider {
public function register(){
$this->app->bind('seotools.metatags', function(){
return new XyzSEOMeta($this->app['config']);
})
}
}
You are all set. Hope this will help.
EDIT:
Above mention method will just override the single class. If you want to change the logic of more than one class. Best way is to fork the project. Change the code and push it to your fork. Use forked project as your composer dependency. Follow the link to know how to use private repository as composer dependency : https://getcomposer.org/doc/05-repositories.md#using-private-repositories
It's very simple just like we overriding any parent class function in derived class.
Create your own class and extend your package class SEOMeta and re-declare function that you want to override and put your logic inside. Don't forget to use namespace of your package class SEOMeta at upper your custom class.
Now use your custom class instead of package class inside your controller.
e.g
use Path\to\SEOMeta;
class urclassname extends SEOMeta{
public function overridemethodname(){
// put ur logic here.
}
}

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 :)

Resources