Laravel: Difference between View::share() and View::composer() - php

In relation to the question Passing default variables to view, to pass variables available among all views, is there a technical or functional difference between the use of View::composer():
View::composer('*', function($view) {
$thundercats = 'Woooooohh!!';
$view->with('thundercats', $thundercats);
})
in the filters.php file or the use of View::share() in the BaseController.php file:
public function __construct {
$thundercats = 'Woooooohh!!';
View::share('thundercats', $thundercats);
}
I've only recently learned about View::share() and find it exceptionally intruiging although I've already started using the former in another project.
Edit:
My first assumption is that the former is a file (filters.php) while the the latter is a class (BaseController.php). With this in mind, I'm guessing a class is much better? Although, I'm not quite sure why at this point. :)

Technically they are not at all alike. View::share simply sets a variable, while View::composer is a callback function.
Let me explain in greater detail:
View::share is really straight forward it sets a variable which can be used within any of the views, think of it like a global variable.
View::composer registers an event which is called when the view is rendered, don't confuse it with a View::creator which is fired when a view is instantiated.
View::composer / View::creator can both be used as a class which is well documented.
While these give you the ability to pass additional data to a view, they also give you to ability to do a lot of other things, for example they could:
Aid in debugging a view
Log information about views
Be used to create custom caching (probably not a great idea, but possible)
These are just some examples of what could be possible using View::composer and View::creator.

View::composer('*', callback());
Means that the callback will be called for all views (*).
View::share
Means that a variable will be shared with all outputed views.
Because the first is in filters.php, it'll apply for all routes.
The second is in a controller contructor, so it'll apply for all views triggered by this controller.
One last thing: when overriding a constructor, it's a good pratice to allways call the parent constructor with this code:
parent::_construct();

Related

Is the current way of building in PHP, the html layout structure possible?

I started not long ago with building my own mvc structure in PHP.
I have seen many people include in home.php page the header.php and footer.
I am currently stuck in finding a solid way to render my views.
I would like to know if it is even possible the way I am combining the header.php, home.php, and footer.php because this is not working for me which made me curious if there is even a native clean way of working with PHP layout structures?
any info would be appreciated. I try my best to explain the code below.
now working with this MVC structure. the router currently checks the req url and gives the controller
example of very basic router:
2 parameters: first the route, second the controller witch should render the view.
public function get($route, $controller) {
if($_SERVER['REQUEST_METHOD'] !== 'GET') {
return false;
}
$uri = $_SERVER['REQUEST_URI'];
if($uri === $route) {
$this->handled = true;
return include (controllers . $controller);
}
}
the routes that are being called:
$router = new Router();
$router->get('/', 'home.contr.php');
$router->get('/home', 'home.contr.php');
$router->get('/about', 'about.contr.php');
$router->get('/portfolio', 'projects.contr.php');
the router calls the controller and in my controller I render the view. with CreateView function
Home.contr.php:
class Home extends Controller {
public function __construct() {
Home::CreateView('home');
}
}
$home = new Home();
the extend controller that should implement the views/layout:
class Controller {
public static function CreateView($viewName) {
require_once views . 'components/header.php';
require_once views . "$viewName.php";
require_once views . "components/footer.php";
}
}
thank you in advance.
Limitations
MVC is a very broad topic with much different understanding so that by the term alone this is hard to answer properly - even in context of a PHP application. You normally refer to an existing implementation of MVC which is not the case here as you want to do it your own (Hint: Read code of existing implementations that is available and about you want to learn more).
Discussion
With that being said, you can find some practical "first next steps" suggestions at the end of the answer.
But I read your question as well that you're concerned about the HTML templates and perhaps also what this has to do with how you wrote your example. So I start a non-binding discussion about the View and then go over to Route and Controller. The Model layer I've kept out of the discussion mainly, at least for that you have to face third-party libraries as otherwise your application structure would not be a good host for broad functionality, this is touched by autoloading.
I have no authority in MVC, I just used some of the early implementations in PHP and applications influenced by them but never implemented it fully. So don't read out any suggestion from the discussion regarding it, it is merely about your example and what came to my mind in specific to PHP. At the end of the day it is you who will find the answer to your own programming questions.
Let's go.
A suggestion/assumption first: You certainly don't want to implement the view creation with the Controller class but with a View class. It would not change much just that the controller does not "care" about it (MVC = Model View Controller).
You can refactor (change) your code by introducing a View class and move the Controller::createView() to View::create() (compare: extract/move method).
Then using require_once - while it may work - it would only work if the template file is only used once. This is certainly not what you want to express here (and later in the discussion we'll see that with the existing example this can also more easily happen than perhaps intentionally thought), instead use require (or include depending on how you want to handle errors) as they will always execute the code in the file (for potential problems redefining controllers, see later in the discussion first routing and then second autoloading).
Apart from obvious code errors (typos) you'd need to address to get it to run (which is a good opportunity to explore PHP error handling and monitoring for your application) you still need to pass the output data of the controller to the view.
This can be so called view models or just objects (in the broader sense) holding the data to be viewed (rendered by the view). Just require/include-ing the (HTML layout) template files won't suffice as they may contain the HTML structure but not the controllers' output data. On the level of the templates this is typically in variables, e.g. the title of the hypertext document:
<title>
<?= htmlspecialchars($title, ENT_QUOTES | ENT_HTML5) ?>
</title>
If this would be the body of a function, the function definition would be:
function outputHeader(string $title): void {
# ...
}
As we don't have a function by requiring the template files, this is just exemplary. However we could create a generic function that handles requiring a template file and passing the variables to the template (compare include_helper()). In that layer you can also do some ground level error handling (try {} catch (Throwable $throwable) {} etc.). For starters you could collect and group such code in the View class.
What you also likely want to prevent is to bind the view within the controllers' constructor method (Controller::__construct(), ctor in short). It forces you to have a named view - and always the same - makes the controller dependent on that view.
That would mean you couldn't configure any view to any controller. While it wouldn't make sense in most cases to allow an any-to-any relationship here in the concrete practice, it allows you to actually have layer boundaries and to not couple things too tightly (compare: Spaghetti Code 1) and to write code on a higher level (in grade of abstraction, compare Layer of Indirection).
An example in a HTTP application would be to do content negotiation. This would happen on the level of request processing (more in the Router in your example), e.g. a HTTP client requests JSON instead of HTML. Now the HTML templates wouldn't fit here. But the Controller could still do the work if not the view template would be hard-encoded.
To keep things more flexible (so you can use it to a greater extend), one benefit of the MVC model is to use (and to a certain degree somehow pass the result of) the Model by the Controller to the View. It helps you define clear boundaries between those three and keep them more apart from each other (less coupled).
The routing then could negotiate and decide what to bring together, similar as in your example for the Controller already but extended with the View (template), each route could be assigned a layout/template.
As this would work quite the same as with the controller - just for the view - let's see where the current Controller not only is standing in the way for the view but already for the routing (if you find a flaw or bug, look around, often they are not in a single place and alone).
While you already configure the routes in the router, the actual routing you've put in the Controller base-class (Controller::get($route, $controller)). Similar to the __construct() method, this makes the Controller implementation dependent on the Route and even implements the routing. This is pretty convoluted and will certainly become awkward. There is also the problem when you add more routes you loose control which one matches as the matching is done within each Controller etc. . In short, while the code may be functional, it just seems to me it can benefit to be at a different place. As it's about the routing, first place that comes into my mind would be the Router itself. The Router then could do the actual work, "do the routing":
$router = new Router(); # <-- bootstrap
$router->get('/', 'home.contr.php'); # <-- prepare
$router->get('/home', 'home.contr.php'); # <-- prepare
$router->get('/about', 'about.contr.php'); # <-- prepare
$router->get('/portfolio', 'projects.contr.php'); # <-- prepare
$router->route(); # <-- do the work here
The Routers get() method then could stay the same from the outside but you would just store the routes inside and when you invoke the route() method, that configuration is matched against your request implementation.
You could then extend the router configuration with the view name.
It would be then that you still have bound a route to a controller and a view, however you have a central location where this is done (configured/parameterized). Controller and View are more independent to each other and you can concentrate more with their own implementation than the overall wiring which now moved into the router.
Finally while being here, what your example also shows is its dependence on the file-system, you have a certain file-naming convention for the controllers and also the view templates. While it is implicitly necessary to place the code into files, at least in your example on the level of the controllers you can already rely on PHP autoloading. While you want to write everything yourself (e.g. not using a ready-made MVC library), I'd still suggest to make use of some standards, like Autoloader (PSR-4) and as being inherently lazy, make the app a Composer project (it has a composer.json file) as Composer allows you to configure the autoloader and there is a well-defined process developing with it (you can also bring in more easily third-party libraries which you'll certainly need within your application logic, so this is just forward-thinking in a good sense, just start without any requirements just using the Composer autoloader).
So instead of hard-linking controller PHP file-paths, you could say instead that a controller basically is a class definition with at least a single method that the router is able to call. With the autoloader in action, the routing configuration would only need to reference that class/method and PHP then would take care to load the class. This could be done as strings (lazy-loading) or more explicit with the First class callable syntax (PHP 8.1). A good middle-ground for starters perhaps is to have one Controller per class and require to have it an interface so that you have a contract (compare: programming against interfaces 1, 2, 3, 4, 5, 6, 7 etc.). You can then simply pass the class-name and handle the instantiation in the route() method.
$router->get(
/* route */ '/',
/* $controller */ MyApp\MVC\Crontroller\Home::class,
/* $viewName */ 'home'
);
<?php
namespace MyApp\MVC\Controller;
class Home implements Interface {
# ...
}
<?php
namespace MyApp\MVC\Controller;
interface Interface {
public function invoke(InputParameter $params): InvocationResult
}
The route() then could check for the interface to verify some class can be used as a controller (instanceof) and would know how to invoke() the controller by passing the input parameters to receive the result that can be further delegated to the template layer.
This is made possible by also introducing the InputParameter and InvocationResult implementations (classes/interfaces) that help to define the layer boundary of the Controller part.
You can then do something similar for the View layer however the output comes relatively late and you're perhaps not yet settled with it (and you may have different template "engines" depending on use-case) so I would leave it more thin and less engineered and try with the Controllers first and do the delegation in the routing until you learn more about your actual requirements (Session handling, Authentication, Content-Negotiation, Redirects etc.).
At the end of the day you have to make your own decisions here.
Next Steps Suggestions
Add at least one test-script that you can run from your development environment "with a single key-press / click" and simple OK/Fail result (e.g. a simple PHP script that you execute in the shell)
Think about how to improve the error handling so you learn about defects faster (e.g. introduce exception and
Fix the bugs first, your code should actually run first of all (it might not produce the intended results in full but it should at least run - your example does not)
Init Composer / add composer.json to your project
Then change the code to your liking which can benefit having it under test first (compare Unit Tests)

difference between using the session via an HTTP request instance versus using the global session helper in Laravel

I could not find any information related two types of session access methods.
$request->session() from HTTP request instance and session() from session helper in Laravel 5.3 .
Is there any difference or which one to use when ?
How to send a get request to below controller method when using P.H.P unit
public function testMyMethod(Request $request){
$userExist = $request->session()->exists('user_id');
}
The Service Container is the core of the Laravel architecture. All services, components, and dependencies are registered there, and you can ask for any of them whenever you need it.
But Laravel provides more than one way to "ask for it". You have the global helper functions, you have facades, you have the "proper", "purer" injection of the component instance in the method signature. I think a big part of the philosophy of Laravel is a clean, easy, intuitive API. And in this case, it can be left up to your personal preference to define what "clean and easy" is, and whatever matches your style will be, by definition, intuitive to you.
I know there have been heated debates in the PHP community as to which method is "best", facades have been controversial, traditional dependency injection OOP purists say the only right way may be injecting the object with the controller method signature...
In the end, any of this various methods just grab the object from the same service container bag. It makes no difference performance-wise (I bet two function calls of indirection won't hit your performance) or otherwise. So, use whatever suits your style better. I personally do the "proper" injection anyway if I'm in a typical controller, but maybe I use the global helper if it makes for a cleaner, more readable code in its context.
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\Session\Session;
use Facades\Illuminate\Contracts\Session\Session as SessionRealTimeFacade;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Request as RequestFacade;
use Illuminate\Support\Facades\Session as SessionFacade;
use PHPUnit\Framework\Assert;
class TestController extends Controller
{
public function sessionTest(Request $request, Session $session)
{
$options = [
$request->session()->get('_token'),
session()->get('_token'),
session('_token'),
$session->get('_token'),
SessionFacade::get('_token'),
SessionRealTimeFacade::get('_token'),
app('session')->get('_token'),
];
array_reduce(
$options,
function ($one, $other) {
Assert::assertEquals($one, $other);
return $other;
},
array_shift($options)
);
return 'All tests passed!';
}
}
And by the way, as you can see you have more than just 2 options :)
Of course, this doesn't apply to sessions only, the same goes for getting the request data, getting DB connection, and so much more.
(Also, I think you don't have the real-time facades in 5.3 yet)
Laravel Documentation
According to the Laravel Docs - https://laravel.com/docs/5.3/session - there is 'little practical difference between using the session via an HTTP request instance versus using the global session helper' and that both are testable.
->assertSessionHas($key, $value = null);
Laravel Up & Running - Matt Stauffer
In his book (page 320-), Matt talks through the different ways available to access session information:
Using the Session facade
session()->get('user_id');
Using the session() method on any given Illuminate Request object
Route::get('dashboard', function(Request $request) {
$request->session()->get('user_id');
});
Inject an instance of Illuminate\Session\Store
Route::get('dashboard', function(Illuminate\Session\Store $session) {
return $session->get('user_id');
});
Using the global session() helper
$value = session()->get('key);
$value = session('key);
His final comment is: If you're new to Laravel and not sure which to use, I'd recommend using the global helper.
So, I wouldn't worry about the differences and just go with what 'feels right' for you. If you have no preference either way, go with Matt Stauffer's recommendation.
Ultimately, the way that you normally use the two options, there is no practical difference between $request->session() and session(). However, they do not technically return the same objects.
session() will return the SessionManager. $request->session() will return the session Store.
The Store is actually the object that contains the session data.
The SessionManager is the object that manages the available session Stores. Most applications will only ever use one session store (the default one).
The reason why they seem to act as the same object is that when you call a method on the SessionManager that doesn't exist, it forwards that method call to the default session Store.
So, in your example, you use the exists() method. This method does not exist on the SessionManager. So, if you were to call session()->exists('user_id'), it would forward that call down to the default session Store, and it would be equivalent to calling $request->session()->exists('user_id').
One could argue that directly using $request->session() is more appropriate when attempting to access the session data, since that is what is returned, and doesn't require the additional forwarding function call. However, it is up to you to weigh the pros and cons to determine which method to use.
$request->session() and session() both are same thing.
There are two primary ways of working with session data in Laravel: the global function in session() helper and via a $request instance.
you can use it like this
public function testMyMethod(Request $request){
//$userExist = $request->session()->exists('user_id');
$userExist = $request->session()->has('user_id');
}
Actually native PHP session is a ready solution versus a better implementation from Laravel and Symfony that had put more thought into it. Read the explanation at Symfony Session configuration manual entry and you will understand i think. So there's a difference, but in the way it is thought and implemented. From the point of view from performance i think there isn't much. I will let you take your conclusions. Another entry that helps is at PHP manual

Using `__destruct` to implement default routing?

I'm updating a PHP framework I've written. It used to just use a default behavior for routing. For example consider a case where the request goes to domain.com/package/controller/method...
$url = ["package", "controller", "method"];
//Check if package exists...
//Check if controller exists in package...
//Check if method exists in controller...
This is all well and good, and works perfectly. However, I wanted to add some additional functionality to my router. That functionality being the ability to define custom routes, and pass an anonymous function which does whatever you want.
However, supposing that the request does not match any of the user-defined routes, I want to use the default functionality I have now to check if there are additional possible routes. That way I can update old projects with the new framework and not have them break, and additionally...I just like this default behavior because most of the time routes are not that complicated and defining routes feels like a violation of DRY to me.
The problem is that I don't want to pass the user-defined routes as an array to the object constructor. Rather, I want the user to call them as methods on the base application object similar to how laravel or express handles this.
The problem is that I want the default route checking to happen AFTER the user's defined routes have been checked not before. This quasi-code might help you understand what I mean...
class App
{
__construct
{
//Check Default Routing
}
private function get()
{
//Get Request
}
private function post()
{
//Post Request
}
private function put()
{
//Put Request
}
private function delete()
{
//Delete Request
}
}
app::get();
In the above case, the default routing would take place before the user-defined routes are called. I looked at the PHP consrtuctor/destructor page and learned about __destruct. However, after reading this question I'm a little bit unsure this would work.
PHP.net says...
The destructor method will be called as soon as there are no other
references to a particular object, or in any order during the shutdown
sequence.
The first part of that explanation sounds like exactly what I want. I.E. as soon as all of the methods have been called on the application object, we'll run the __destruct function which will check if the user-defined routes were fruitful, and if not, check if the default routing system yields any results.
The problem is that I'm not sure if this is bad practice, or simply won't work. Can I require a file, set my controller, and then call a method on that controller from within __destruct? Are there limitations that would effect the code within these controllers? Supposing that there is a problem using __destruct this way, what are my alternatives, keeping in mind I don't like either of these solutions...
Having the user call the default routing as a method at the end of their script.
Passing routes in as arrays to the constructor.
I think you're confused here. Take note of this from the PHP Manual
The destructor method will be called as soon as there are no other references to a particular object, or in any order during the shutdown sequence.
To put this a different way, there's two reasons to call a destructor
The class is being garbage collected. In other words, you've overwritten or unset all the references to the class instance. This means you can't directly call this class anymore
The PHP script has reached its end and the thread is shutting down.
In other words, there's nothing left for the class to do. But in your own statement you say this
The first part of that explanation sounds like exactly what I want. I.E. as soon as all of the methods have been called on the application object, we'll run the __destruct function which will check if the user-defined routes were fruitful, and if not, check if the default routing system yields any results.
There is no "and" here to work with. That's the point. In fact, there's very few places you would use this.
What you need is to think in layers. So you'd have a controller layer that you'd call to check the methods. In turn, that controller opens a new layer that checks user functions. That class or method should return something or throw an Exception if it fails. On failure it can then try to use default methods. This is how you need to structure your program. Trying to use a destructor to do this would likely only confuse people. Make the data flow explicit, not implicit (which is what magic methods do).

Short functions from a Codeigniter's library

I got a Tank Auth library installed in my Codeigniter package, the problem is that I don't like how to is_logged_in functions need to be called because it's simply long and not so friendly, as I need to use:
$this->tank_auth->is_logged_in()
Everytime I want to check if user is logged in...
So is there a way to make it shorter? By saying shorter I mean something like $this->logged();?
I would really appreciate if someone could help me.
Thank you.
Head into the tank_auth library and define a new public function:
public function logged(){
return $this->is_logged_in();
}
You can now access it with $this->tank_auth->logged();
If you want to shorten the name of tank_auth, you'll have to rename the class and the filename.
UPDATE:
The more important question is, why are you calling this so many times that it is becoming an annoyance? You should only have to write it once if your code follows the Don't Repeat Yourself (DRY) principle.
Have a look at Phil Sturgeon's blog post entitled Keeping It DRY. He will show you how to write a base controller that all your controllers will inherit from. If you write the login check in the constructor of the base controller, you don't have to write it in every controller.
I would argue against doing this as the method logged() in your instance lacks context. However, if you wanted to do this, you could write a base controller which has a logged() method which ends up returning $this->tank_auth->is_logged_in(). All controllers would inherit from this base controller, which isn't a bad idea to begin with.
If you're using libraries, you could implement a similar pattern in them.
You probably don't want to edit anything in the TankAuth library if you didn't create it as doing so affects the updatability of the library. Instead, you might add a method to your controller called logged and have it reach out to Tank Auth. Although, I would choose a better name for your function as pointed out by a previous answer.
Create or edit your controller base class to have something like this:
function is_logged() {
return $this->tank_auth->is_logged_in();
}
Then you may call it like so: $this->is_logged();

MVC: Why bother "sending" data to the View

I'm very new to MVC, and so I've been scouring the net in an attempt to build my own framework to get a real understanding on how the whole concept works.
Anyway, almost all tutorials out there that deal with MVC always seem to assign data that needs to be displayed in the view to an intermediary variable that is THEN used in the view.
My question is, why bother with that extra step?
Most MVC implementations end up including the view WITHIN the controller... so if that's the case, why waste time/memory/cpu cycles to create an intermediary variable/array that is then passed to the View when the View ends up being included with the controller at the end.
Would it not make more sense to simply use the Controller variables directly in the View itself?
Here's a code example to hopefully clarify what I mean:
class News_Controller
{
public function main(array $getVars)
{
$newsModel = new News_Model;
//get an article
$article = $newsModel->get_article($getVars['article']);
//create a new view and pass it our template name
$view = new View_Model($this->templateName);
//assign article data to view
$view->assign('title' , $article['title']);
$view->assign('content' , $article['content']);
$view->render();
}
The render function is basically just an include to bring the View into the Controller to be displayed down the chain. If that's what's going on, one could simply use $article directly in the View rather than go through the hassle of assigning variables to the View.
Keep in mind that PHP copies on write. So there is no major performance hit to a simple variable assignment.
As already mentioned, scope is a big issue here. The view is a separate entity from the controller and doesn't have access to its data. Of course, you could pass an instance of the controller to the view, but that's creating an unnecessarily too strict of coupling between the two. The view should be able to work independent of the controller.
So by explicitly assigning data to the view you decouple the two. You will tend to write better and cleaner code as a result.
Second, the process of assigning data to a view could do some data sanitizing or other extra work. For instance, in my framework, I consider all data passed to an HTML view as unsafe. When data is passed to the view (unless explicitly marked as safe) it is encoded via htmlspecialchars.
Finally, you can always assign objects or arrays to the view:
$view->assign('article', $article);
If you do this you generally don't need to assign very much stuff. (And if you do, perhaps your page is doing too many different things.)
MVC is a very loose categorization. You are describing one way it could work. It's also possible that the variables you use in your controller may not be intended to be used as-is within your view. You may have some sort of template processor that takes in data from the controller, alongside a specially marked-up view template, and spits out the result. Or you may be calling functions/methods from within your view that return ready-made markup.
your include inherits everthing from the render() method's variable scope, but the render() method does not inherit anything from the controller's variable scope.
class foo {
public function bar() {
echo $somevar;
}
}
$somevar = 'test';
$foo = new foo();
$foo->bar();
this code will echo nothing and give you a warning that $somevar has not been defined (if your error reporting is set to show warnings). the reason for this is because methods and functions do not inherit the scope of where they were called from.
php.net/manual/en/language.variables.scope.php
php.net/manual/en/language.oop5.visibility.php
Because of the scope the controllers variables are in. Unless you make everything global (really bad idea) your concept won't work.

Categories