Panoply
Panoply

Reputation: 433

Models/Controller variables - Laravel

I have been using Query builder in my controller, however want to move into utilising eloquent and query builder. I want to know the best practice of passing variables between the both of them. I have been through the documentation but still having trouble.

Here is how I am doing it in my controller:

public function getRetailers($city) {    
    $locations = DB::table('retailers_listings')
        ->orderBy('country', 'asc')
        ->Where('city', $city)
        ->get();

        $this->layout->header = $city;
    $content = View::make('retailers.stores')
        ->with('header', $this->layout->header)
        ->with('store_listings', $locations)

        if (Request::header('X-PJAX')) {
        return $content;
        } else { 
            $this->layout->content = $content; 
        } 
    }

How would I do the above with eloquent and in a model? How would I pass the variable $city to the model as it comes in from the view. A bit more in depth advice then what the documentation gives me would be great.

Upvotes: 1

Views: 8230

Answers (1)

tliokos
tliokos

Reputation: 3636

Inject your model as a dependency in your controller.

In this example i refer to a listing model for simplicity.

class HomeController extends BaseController {

    protected $listing;

    // Type hint your model. This is laravels automatic resolution

    // It will automatically inject this dependency for you

    public function __construct(Listing $listing) 
    {
        $this->listing= $listing;
    }

    public function index()
    {
        $listings = $this->listing->whereTitle(Input::get['title'])->get();

        return View::make('listing' , compact($listings));
    }

}

An even better approach is to use repositories that will let you swap storage implementations.

Check the answer in this post for more info.

As a matter of fact you can use the Listing model in your controller without dependency injection like

Listing::all();

but the aforementioned approach makes it a little clearer.

UPDATE

I don't know exactly how your applications is structured, so i will try to mimic. You can change the files and classes name to match your implementation.

Preparation Steps

  1. Under your app folder create a new folder called Acme (or whatever you like). This folder lives in the same level with controllers, models, views etc.
  2. Inside the Acme folder create another folder called Repositories
  3. Inside the Repositories folder create a new file called RetailerRepository.php
  4. Edit the composer.json file in your root directory and add a prs-0 section, in order to autoload our news classes

    "autoload": {
    "classmap": [ "app/commands", "app/controllers", "app/models", "app/database/migrations", "app/tests/TestCase.php" ], "psr-0": { Acme": "app/Acme" } }

  5. In your console run composer dumpautoload -o and you're done

Now let's do some code

I assume your ORM is

class Retailer extends Eloquent{}

Edit your Acme/Repositories/RetailerRepository.php to look like this:

<?php namespace Acme/Repositories

use Retailer;

class RetailerRepository {

  public function getLocations($city) {

    return Retailer::whereCity($city)->orderBy(‘country’, ‘asc’)->get();

  }    
}

Edit your controller to look like this. I user generic names but you can switch to your own.

<?php

use Acme/Repositories/RetailerRepository;

class RetailersController extends BaseController {

  protected $repo;

  public function __construct(RetailerReposiroty $repo) {

    $this->repo = $repo;
  }

  public function index($city) {

    // I assume that the route is 

    // Route::get('retailers/{city}','RetailersController@index')

    $locations = $this->repo->getLocations($city);

    // I keep it simple here but you can do whatever you want

    return View::make('retailers.stores')->with('stores', $locations);
  }

}

As you can see now, your controller does not know where data comes but it knows how to access it. It doesn't have to know that your data is available in MySQL. It's just fine by knowing that is available somewhere. Also, by structuring your app this way, now you're able to use the repository functionality from any controller you like, just by injecting the repository as a dependency in the constructor. In a complex app you would probably use a RetailerRepositoryInterface and multiple concrete implementations but let's keep it simple here.

Now, is there a requirement for your application to provide e.g. billing capabilities. Create a new folder under Acme called Services and define there your service providers. Don't bloat your controllers with business logic. Structure your Acme folder however you like to match your needs. It's your app!

There is a common misconception that the model is simply a class (E.g. the Retailer class that extends Eloquent in our case). This misconception, along with the expression “Fat models skinny controllers” has led many people think that they have to get all business logic out of controllers (which is absolutely correct) and place it inside a simple class (which is absolutely wrong).

Your Model (the M in MVC) it's not simply a class. It's a layer that contains domain entities, data abstractions, services providers etc.

Hope i helped.

Upvotes: 5

Related Questions