Asked  2 Years ago    Answers:  5   Viewed   78 times

I'm new to Laravel and using PHP namespaces in general. I didn't run into any problems until I decided to make a model named File. How would I go about namespacing correctly so I can use my File model class?

The files are app/controllers/FilesController.php and app/models/File.php. I am trying to make a new File in FilesController.php.

 Answers

1

Namespacing is pretty easy once you get that hang of it.

Take the following example:

app/models/File.php

namespace AppModels;

class File {

    public function someMethodThatGetsFiles()
    {

    }
}

app/controllers/FileController.php

namespace AppControllers;

use AppModelsFile;

class FileController {

    public function someMethod()
    {

        $file = new File();
    }
}

Declare the Namespace:

namespace AppControllers;

Remember, once you've put a class in a Namespace to access any of PHP's built in classes you need to call them from the Root Namespace. e.g: $stdClass = new stdClass(); will become $stdClass = new stdClass(); (see the )

"Import" other Namespaces:

use AppModelsFile;

This Allows you to then use the File class without the Namespace prefix.

Alternatively you can just call:

$file = new AppModelsFile();

But it's best practice to put it at the top in a use statement as you can then see all the file's dependencies without having to scan the code.

Once that's done you need to them run composer dump-autoload to update Composer's autoload function to take into account your newly added Classes.

Remember, if you want to access the FileController via a URL then you'll need to define a route and specify the full namespace like so:

Route::get('file', 'App\Controllers\[email protected]');

Which will direct all GET /file requests to the controller's someMethod()

Take a look at the PHP documentation on Namespaces and Nettut's is always a good resource with this article

Monday, October 3, 2022
1

Although you use dependency injection on Option #1, your controller is still coupled with the Eloquent ORM. (Note that i avoid to use the term Model here because in MVC the Model is not just a class or an object but a layer. It's your business logic.).

Dependency Injection allows for Dependency Inversion but they are not the same thing. According to the Dependency Inversion principle both high and low level code should depend on abstractions. In your case the high level code is your controller and the low level code is the Eloquent ORM that fetches data from MySQL, but as you can see none of them depends on abstractions.

As a consequence, you are not able to change your data access layer without affecting your controller. How would you go about changing for example from MySQL to MongoDB or to the File System? To do this you have to use repositories (or whatever you want to call it).

So create a repositories interface that all your concrete repository implementations (MySQL, MongoDB , File System etc.) should implement.

interface PostRepositoriesInterface {

    public function getAll();
}

and then create your concrete implementation e.g. for MySQL

class DbPostRepository implements PostRepositoriesInterface {

    public function getAll()
    {

        return Post::all()->toArray();

        /* Why toArray()? This is the L (Liskov Substitution) in SOLID. 
           Any implementation of an abstraction (interface) should be substitutable
           in any place that the abstraction is accepted. But if you just return 
           Post:all() how would you handle the situation where another concrete 
           implementation would return another data type? Probably you would use an if
           statement in the controller to determine the data type but that's far from 
           ideal. In PHP you cannot force the return data type so this is something
           that you have to keep in mind.*/
    }
}

Now your controller must type hint the interface and not the concrete implementation. This is what "Code on an interface an not on implementation" is all about. This is Dependency Inversion.

class HomeController extends BaseController {

    public function __construct(PostRepositoriesInterface $repo)
    {
        $this->repo= $repo;
    }

    public function index()
    {
        $posts = $this->repo->getAll();

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

}

This way your controller is decoupled from your data layer. It's open for extension but closed for modification. You can switch to MongoDB or to the File System by creating a new concrete implementation of PostRepositoriesInterface (e.g. MongoPostRepository) and change only the binding from (Note that i don't use any namespaces here):

App:bind('PostRepositoriesInterface','DbPostRepository');

to

App:bind('PostRepositoriesInterface','MongoPostRepository');

In an ideal situation your controller should contain only application and not business logic. If you ever find yourself wanting to call a controller from another controller its a sign that you've done something wrong. In this case your controllers contain too much logic.

This also makes testing easier. Now you are able to test your controller without actually hitting the database. Note that a controller test must test only if the controller functions properly which means that the controller calls the right method, gets the results and pass it to the view. At this point you are not testing the validity of the results. This is not controller's responsibility.

public function testIndexActionBindsPostsFromRepository()
{ 

    $repository = Mockery::mock('PostRepositoriesInterface');

    $repository->shouldReceive('all')->once()->andReturn(array('foo'));

    App::instance('PostRepositoriesInterface', $repository);

    $response = $this->action('GET', '[email protected]'); 

    $this->assertResponseOk(); 

    $this->assertViewHas('posts', array('foo')); 
}

EDIT

If you choose to go with option #1 you can test it like this

class HomeControllerTest extends TestCase {

  public function __construct()
  {
      $this->mock = Mockery::mock('Eloquent', 'Post');
  }

  public function tearDown()
  {
      Mockery::close();
  }

  public function testIndex()
  {
      $this->mock
           ->shouldReceive('all')
           ->once()
           ->andReturn('foo');

      $this->app->instance('Post', $this->mock);

      $this->call('GET', 'posts');

      $this->assertViewHas('posts');
  }

}
Thursday, December 15, 2022
 
5

I guess you try to show the search results after searching. The problem is this line.

return Redirect::to('property/search', compact('properties'));

After you get the search result you should call a view, not redirect.

return view('property.search', compact('properties'));

But make sure you have the view file.

Source

Monday, September 5, 2022
 
2

After setting AllowOverride to all in apache2.conf and enabling mod_rewrite with the command a2enmod rewrite it all works.

Thursday, August 4, 2022
 
3

To return a cookie with a view, you should add your view to a Response object, and return the whole thing. For example:

$view = View::make('categories.list')->with('categories', $categories);
$cookie = Cookie::make('test-cookie', 'test data', 30);

return Response::make($view)->withCookie($cookie);

Yeah, it's a little bit more to write. The reasoning is that Views and a Response are two separate things. You can use Views to parse content and data for various uses, not necessarily for sending to the browser. That's what Response is for, and why if you want to set headers, cookies, or things of that nature, it is done via the Response object.

Thursday, November 3, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 

Browse Other Code Languages