My ide typhints a few different namespaces for rendering a view in my controller and i'm not sure which one i'm supposed to use:
class PostsController extends Controller
{
public function index() : View
{
return view('posts.index');
}
}
The "view" function returns multiple types:
#return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
So which one am i supposed to use? \Illuminate\Contracts\View\View or \Illuminate\Contracts\View\Factory
What is the difference?
Why is it returning two different types instead of one? I code php in a strict way because i prefer it for readable and less error prone code, in my opinion this is a bad way of doing it and as you can see is causing confusion; a method should only be allowed one return type, create multiple methods if need be.
EDIT
Thank you for your input everyone, i have come up with the following that allows me to use the facade and the contract without producing typhint errors in my ide:
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\View\View as ViewContract;
use Illuminate\Support\Facades\View as ViewFacade;
/**
* Class PostsController
* #package App\Http\Controllers
*/
class PostsController extends Controller
{
/**
* #return ViewContract
*/
public function index() : ViewContract
{
return ViewFacade::make('posts.index');
}
}
So i can call View:make() and return the contract that View:make() returns.
EDIT 2
Using the view() helper i can condense further, i am aliasing with ViewContract just for my benefit of knowing which namespace i'm using:
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\View\View as ViewContract;
/**
* Class PostsController
* #package App\Http\Controllers
*/
class PostsController extends Controller
{
/**
* #return ViewContract
*/
public function index() : ViewContract
{
return view('posts.index');
}
}
I’ll try and address each of your questions.
In your instance, type-hint Illuminate\Contracts\View\View or the concrete implementation (Illuminate\View\View).
I’ll cover this in 3.
You’re using the view global helper function. It can return different types because, well, it can. If you don’t pass a parameter to view() then it will return a view factory instance. If you do pass a parameter (like you have in your usage), then it will return an instance of the view named by the first parameter (if such a view exists). So that’s why the view() helper function is typed to return multiple different types. Because depending on how you use it, it can return a different type.
You mean: \Illuminate\View\View.
public function index(): \Illuminate\View\View
{
return view('posts.index');
}
view() is a helper of Illuminate\Support\Facades\View facade.
return view('posts.index');
Same as :
use Illuminate\Support\Facades\View;
return View::make('posts.index');
See the documentation of Creating & Rendering Views
Related
I am getting this error when trying to use the Str::limit in views
ErrorException
Undefined property: Illuminate\Pagination\LengthAwarePaginator::$body (View: C:\Users\USER\Desktop\laravels\qna\resources\views\questions\index.blade.php)
here is the code
<div class="media-body">
<h3 class="mt-0">{{ $question->title }}</h3>
{{ Str::limit($questions->body, 250) }}
</div>
Here is the controller
namespace App\Http\Controllers;
use App\Models\Question;
use Illuminate\Http\Request;
// use Illuminate\Support\Str;
class QuestionsController extends Controller
{
// use Str;
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$questions = Question::latest()->paginate(5);
return view('questions.index', compact('questions'));
}
...
}
I get his error when "Str" is un-commented
Symfony\Component\ErrorHandler\Error\FatalError
App\Http\Controllers\QuestionsController cannot use Illuminate\Support\Str - it is not a trait
What is the proper method to use Str:: in a view
Write $question->body instead of $questions->body in your view in order to use the object of question not the paginator.
Actually you don't have to use Illuminate\Support\Str in your controller at all , because you use Str class only in your view and it's one of the aliases in laravel , take a look at config/app.php.
By the way … The (use statement) above class only shorten the namespace you must use in your code like so :
use Illuminate\Support\Str;
class QuestionsController extends Controller
{
public function index()
{
Str::limit("Some String");
}
}
But if you don't put this use , your code would be :
class QuestionsController extends Controller
{
public function index()
{
\Illuminate\Support\Str::limit("Some String");
}
}
whereas when we put use statement inside class , it means we are trying to use trait in our class
https://www.php.net/manual/en/language.oop5.traits.php
I don't think you need to add it in your controller, so just add
use Illuminate\Support\Str;
to your model and that should allow you to use it anywhere. And then in your blade you can use
\Illuminate\Support\Str::limit($questions->body, 250)
This is a Laravel solution but I do recommend looking at this thread for a pure PHP answer Limit String Length
string limit with the end of three dots
\Illuminate\Support\Str::limit($clientName,16,'...');
I'm using Laravel version 6.x. I have created one helper function called GraphqlApi.
My helper class will look like below:
<?php
namespace App\Helpers;
/**
* GraphqlApi class
*/
class GraphqlApi
{
public function __construct() {
echo "test";die;
}
}
But whenever I call any function from helper class construct function is not being called.
Is there any way to make it work?
if you always want the constructor to be called when you are calling other methods.
First, you can not have static method.
Second, call the method this way.
$graphqlApi = new GraphqlApi();
$result = $graphqlApi->getGuzzleRequest();
This way the constructor will be called.
I noticed that many of the examples in the Laravel docs seem to have Controllers where the class has only one use/method.
For example, in this part of the doc, they have a UpdatePasswordController class with a single method, update():
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use App\Http\Controllers\Controller;
class UpdatePasswordController extends Controller
{
/**
* Update the password for the user.
*
* #param Request $request
* #return Response
*/
public function update(Request $request)
{
// Validate the new password length...
$request->user()->fill([
'password' => Hash::make($request->newPassword)
])->save();
}
}
Normally, I would put a method called updatePassword() in my UserController class (along with signIn(), signUp(), resetPassword(), etc.), but I'm wondering if it's better to create multiple classes, each with a single action?
Normally class is defined for a single purpose. In laravel , for authentication there is a Illuminate base bundle which is optimized for years.
As an example UpdatePasswordController only responsible for updating password,
AuthController only responsible for authentication.
I prefer you to reserch some MVC best practices
I understand that the default Eloquent\Collection class can be overridden in your model by using the method:
public function newCollection(array $models = array()) {
return new CustomCollection($models);
}
Which works great if I'm using typical queries such as:
Model::where('name', $name)->get();
This is great so I can add methods to the eloquent collection class, such as:
$records = Model::where('name', $name)->get();
$records->toTable();
But if I'm using pagination on the model, for example:
Model::where('name', $name)->paginate(25);
It returns an instance of the class Illuminate\Support\Collection instead of the Illuminate\Database\Eloquent\Collection.
Is there a way of overriding or extending the typical Illuminate\Support\Collection?
I'm trying to add a toTable() method to the returned Collection. I'd rather not have to replace the pagination service provider with my own.
Thanks!!
You will need to replace the pagination service provider, amongst a couple of other classes in the pagination library. By the sound of it you know how to do it this way, but were hoping for another answer, but as I have the code I'll drop it in here for you.
The reason you need to replace these classes/methods is because the files in Illuminate directly reference instances of classes within the Illuminate namespace.
In config/app.php
Replace
'Illuminate\Pagination\PaginationServiceProvider',
With
'ExtendedPaginationServiceProvider',
Create a new file somewhere the autoloader is capable of finding it called ExtendedPaginationServiceProvider.php and place the following in it
<?php
use Illuminate\Support\ServiceProvider;
class ExtendedPaginationServiceProvider extends ServiceProvider
{
/**
* #inheritdoc
*/
public function register()
{
$this->app->bindShared('paginator', function($app)
{
$paginator = new ExtendedPaginationFactory($app['request'], $app['view'], $app['translator']);
$paginator->setViewName($app['config']['view.pagination']);
$app->refresh('request', $paginator, 'setRequest');
return $paginator;
});
}
}
Create a new file somewhere the autoloader is capable of finding it called ExtendedPaginationFactory.php and place the following in it
<?php
use Illuminate\Pagination\Factory;
class ExtendedPaginationFactory extends Factory
{
/**
* #inheritdoc
*/
public function make(array $items, $total, $perPage = null)
{
$paginator = new ExtendedPaginationPaginator($this, $items, $total, $perPage);
return $paginator->setupPaginationContext();
}
}
Create a new file somewhere the autoloader is capable of finding it called ExtendedPaginationPaginator.php and place the following in it
<?php
use Illuminate\Pagination\Paginator;
class ExtendedPaginationPaginator extends Paginator
{
/**
* Get a collection instance containing the items.
*
* #return ExtendedCollection
*/
public function getCollection()
{
return new ExtendedCollection($this->items);
}
}
You'll notice the above returns a new instance of ExtendedCollection. Obviously replace this with your CustomCollection class you refer to in your question.
For others to reference, an ExtendedCollection class may look similar to the below
Create a new file somewhere the autoloader is capable of finding it called ExtendedCollection.php and place the following in it
<?php
use Illuminate\Support\Collection;
class ExtendedCollection extends Collection
{
}
Also, after creating these files, don't forget to run the following in the terminal
composer dump-autoload
If I want to set a variable that my whole controller can access, how do I do it?
Right now, in every function I am setting
$id = $this->session->userdata('id');
I'd like to be able to access $id from any function w/o defining it for each controller. :)
If there's a better way, I'm all ears! I'm a noob!
To elaborate on Koo5's response, you'll want to do something like this:
class yourController extends Controller {
// this is a property, accessible from any function in this class
public $id = null;
// this is the constructor (mentioned by Koo5)
function __construct() {
// this is how you reference $id within the class
$this->id = $this->session->userdata('id');
}
function getID() {
// returning the $id property
return $this->id;
}
}
See the manual for more information on PHP properties and constructors. Hope that helps!
If with global you mean the scope of a single controller, the above answers are correct. If you want to access variables from multiple controllers, I recommend defining a BaseController class and then extending your normal controllers to inherit from that BaseController:
class yourController extends BaseController {}
I use this all the time for both global variables and methods.
Define that very line in a constructor only
You can use this method too inside your controller
function __construct() {
// this is how you reference $id within the class
DEFINE("userId",$this->session->userdata('id'));
}
And Call It As :
function getID() {
// returning the $id property
return userId;
}