I'm developing an API and in the controller the index, store, show, update and destroy methods are all the same except for the Model that is being used.
How would you implement this?
I was thinking about a ActionRepository where I create those methods and will resolve the model somehow. I am not sure how I can reach the model though..
Really would appreciate some feedback on this ;)!
You can do this:
abstract class BaseController extends LaravelController {
protected $repository; // or $model or whatever you need
public function index() { // your logic }
public function show($id) {
// your logic here, for example
return $this->repository->find($id);
// or
return $this->model->find($id);
}
public function create() { // your logic }
public function store() { // your logic }
public function edit($id) { // your logic }
public function update($id) { // your logic }
public function destroy($id) { // your logic }
}
class SomeSolidController extends BaseController {
public function __construct(SomeRepositoryInterface $repository)
{
$this->repository = $repository;
}
}
Related
I have a simple code that displays to the user all of his notifications received from the Database:
$user_notifications = DB::table('notifications')->where('user_id', $this->user->id)->orderBy('id', 'desc')->get();
the problem is that I have too many controllers and functions in them and I don’t want to duplicate this code everywhere, in each function and controller. How can I make the $user_notifications variable global and use it everywhere?
u need to create a NotificationRepository
class NotificationsRepository
{
private Notifications $model;
public function __construct(Notification $model)
{
$this->model = $model;
}
public function findByUserId(int $userId): Collection
{
return $this->model->where('user_id', $userId)->orderBy('id', 'desc')->get();
}
}
Then in Controller action add this repository by autowiring
class SomeController extends Controller
{
public function someAction(NotificationRepository $repository, int $id)
{
$notifications = $repository->findByUserId($id);
}
}
or this way, i dont know how did u use your actions
class SomeController extends Controller
{
public function someAction(NotificationRepository $repository, Illuminate\Http\Request $request)
{
$notifications = $repository->findByUserId($request->user()->id);
}
}
I stumbled upon different articles about implementing Repository pattern in Laravel and all of them making me confused. Everyone of them is putting their weight on "keeping it ORM independent" but no one is actually showing the example code.
I am here with my repository sample structure which I believe is not ORM independent in a way. But I need it with REAL REPOSITORY PATTERN solution where I can change the ORM from Eloquent model to something else like Doctrine. And keep the Business logic separate some way so i no need to change it. currently my Business logic lies in Repository (not recommended i think.)
Basic Questions:
My repository, uses Eloquent method name inside it which will not be there if i change the ORM. $this->model = $shops;
In Controllers and Blade templates what we are playing with is Collections of Eloquent Model. How should we handle them if we change the ORM?
Where to put create/delete methods if not in repository.
Please don't just use Domain Object word because I am tired of
understanding it without an coded example. It will be highly
appreciated if you try to explain [Domain object] using a real code
example by modifying this. [How to return it in controller and use it
in Blade]
Interface:
<?php
namespace Modules\ShopOwner\Entities\Repository\Contract;
interface ShopsRepository {
public function getall();
public function create($data);
public function update($id, $data);
public function delete($id);
}
Eloquent:
<?php
namespace Modules\ShopOwner\Entities\Repository\Eloquent;
use Illuminate\Database\Eloquent\Model;
use Modules\ShopOwner\Entities\Repository\Contract\ShopsRepository;
use Modules\ShopOwner\Entities\Shops;
class ShopsEloquent implements ShopsRepository
{
protected $model;
public function __construct(Shops $shops)
{
$this->model = $shops;
}
public function getall()
{
return $this->model::with('shopadmin')->get();
}
public function create($data)
{
$this->model::create($data);
}
public function update($id, $data)
{
$this->model::findOrFail($id)->update($data);
}
public function delete($id)
{
$this->model::findOrFail($id)->delete();
}
}
Controller:
<?php
namespace Modules\ShopOwner\Http\Controllers;
use App\Http\Controllers\Controller;
use Auth;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Modules\ShopOwner\Entities\Repository\Contract\ShopsRepository;
use Modules\ShopOwner\Entities\Shops;
use Validator;
class ShopsController extends Controller
{
protected $shops;
public function __construct(ShopsRepository $shops)
{
$this->shops = $shops;
}
public function index()
{
$shops = $this->shops->getall();
return view('shopowner::shops.list', compact('shops'));
}
public function store(Request $request)
{
$data = $request->all();
$this->shops->create($data);
return redirect()->route('SO.shops.index')->with('success', __('shopowner::shops.create_success'));
}
public function update(Request $request, $id)
{
$data = $request->all();
$this->shops->update($id, $data);
return redirect()->route('SO.shops.index')->with('success', __('shopowner::shops.update_success'));
}
public function delete($id)
{
$this->shops->delete($id);
return redirect()->route('SO.shops.index')->with('success', __('shopowner::shops.remove_success'));
}
}
Create a base repository first. then extend this repository. Try this way. It may help you
interface BaseRepository{
public function getall();
public function create($data);
public function update($id, $data);
public function delete($id);
}
class BaseEloquent implements BaseRepository{
protected $model;
public function getall(){
return $this->model->get();
}
....
....
}
interface ShopsRepository extends BaseRepository{
}
class ShopsEloquent extends BaseEloquent implements ShopsRepository{
public function __construct(Shops $shops){
$this->model = $shops;
}
public function getall(){
return $this->model::with('shopadmin')->get();
}
}
In web.php I have this route which opens a form:
$this->namespace('Users')->prefix('users')->group(function (){
$this->get('/create' , 'UserController#create');
});
And this route returns an array of countries. I use that array to fill a select box via ajax in the form.
Route::namespace('API')->prefix('api')->group(function () {
$this->get('/get-country-list', 'LocationsController#index');
});
Controller:
app\Http\Controllers\API\LocationsController
class LocationsController extends Controller
{
public function index()
{
return DB::table('countries')->pluck("name","id")->all();
}
...
app\Http\Controllers\Users\UserController
class UserController extends Controller
{
public function create()
{
return view('panel.users.home.create.show');
}
...
How can I call LocationsController#index in create() function?
what is the best method?
you can try return redirect(route('...')); instead of return view() in actions.
update
Because you just want to get Countries list instead of redirection. So do small tuning, separate the data manipulating function from the action function:
protected function getCountries() {
return DB::table('countries')->pluck("name","id")->all();
}
function index(Request $request) {
return $this->getCountries();
}
function create(Request $request) {
$countries = $this->getCountries();
return view('panel.users.home.create.show', compact('countries'));
}
I think you should try a different approach. What you seem to be trying to do is reuse this cumbersome query:
DB::table('countries')->pluck('name', 'id')->all();
That's good! However your index() function is a controller endpoint and which returns a response and isn't really suitable for being reused in other controller endpoints. When I am in a similar situation I usually do one of two things,
1. Extract the code to a protected method and use it in both controller endpoint methods
class UserController extends Controller
{
public function index()
{
return $this->countryNames();
}
public function create()
{
// $countryNames = $this->countryNames():
return view('panel.users.home.create.show');
}
public function countryNames()
{
return DB::table('countries')->pluck('name', 'id')->all();
}
}
2. Create a method on the model, in your case this would involve using the model instead of the DB facade.
class UserController extends Controller
{
public function index()
{
return Country::names();
}
public function create()
{
// $countryNames = Country::names();
return view('panel.users.home.create.show');
}
}
I have a class in Laravel with a class variable that holds and object
class RegisterController extends Controller {
public $company;
When i set the variable in my index method all goes well
public function index($id = null) {
$this->company = new Teamleader\Company;
When I try to access $this->company from another method it returns
null
This is my full code
class RegisterController extends Controller {
public $company;
public function index($id = null)
{
$this->company = new Teamleader\Company;
// returns ok!
dd($this->company);
return view('register.index');
}
public function register()
{
// returns null
dd($this->company);
}
}
Am I missing something?
Thank you!
You are not __constructing() the class, you are just assigning variable inside a function inside a class, which means it is encapsulated into that function inside that class.
So if you would like to make $this->company global in class, you could use
public function __construct() {
$this->company = new Teamleader\Company;
}
In Laravel 5 you can inject a new instance of Teamleader\Company into the methods you need it available in.
use Teamleader\Company;
class RegisterController extends Controller {
public function index($id = null, Company $company)
{
dd($company);
}
public function register(Company $company)
{
dd($company);
}
}
For Laravel <5 dependency inject into the constructor.
use Teamleader\Company;
class RegisterController extends Controller {
protected $company;
public function __construct(Company $company)
{
$this->company = $company;
}
public function index($id = null)
{
dd($this->company);
}
public function register()
{
dd($this->company);
}
}
Dependency injection is better than manual invocation as you can easily pass a mock object to this controller during testing. If you're not testing, maybe someone else will be in the future, be kind. :-)
So here is my controller:
class Search extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->model('search_model');
$this->search_model->search_result = $_POST;
}
public function index()
{
$data['results'] = $this->search_model->get_results();
$this->load->view('search_results', $data);
}
And here is my model:
class Search_model extends CI_Model {
protected $search_query;
function __construct($search_query)
{
parent::__construct();
$this->load->database();
$this->search_query = $search_query;
}
But this doesn't seem to work. What I want to do is pass the posted form ($_POST) to my model, then do stuff with it. But it seems messy to pass $_POST to each method of my model. My plan is to extract the variables sent with $_POST and construct these as properties such as $website_url, $text_query etc..., then call these in methods with $this->website_url;
I'm relatively new to CodeIgniter so just getting to grips with the basics
for your special purpose you can try this code
Controller:
class Search extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->model('search_model');
$this->init();
}
private function init()
{
$this->search_model->init( $this->input->post() );
}
public function index()
{
$data['results'] = $this->search_model->get_results();
$this->load->view('search_results', $data);
}
model:
class Search_model extends CI_Model {
protected $search_query;
function __construct()
{
parent::__construct();
$this->load->database();
}
public function init( $search_query )
{
$this->search_query = $search_query;
}
you have protected $search_query; which you can't access it from your controller.
You either have to change it to public or create getter and setter for it. or just getter depending on your domain/business logic.
And it should have been obvious as you should get an error saying
Fatal error: Cannot access protected property in file some/path/to/file!
Don't put the 'search query' in your model constructor.
Controller:
class Search extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->model('search_model');
}
public function index()
{
if ($this->input->server('REQUEST_METHOD') == 'POST')
{
// you should probably validate/clean the post data instead
// of sending it straight to the model
$results = $this->search_model->get_where($_POST);
}
else
{
// if it's not a post, you probably need something...
// either here, or somewhere in your view to handle empty data
$results = array();
}
$data['results'] = $results
$this->load->view('search_results', $data);
}
Your Model:
class Search_model extends CI_Model {
function __construct()
{
parent::__construct();
$this->load->database(); // <--- you may want to autoload 'database' library
}
function get_where($where)
{
$this->db->where($where);
// add select, order, joins, etc here
return $this->db->get('mytable'); // whatever your table name is
}