Using facade in Models issue - laravel

I am developing a project on Laravel 5.2 , i may use facades (Auth,Request,Cache.. ) in controllers but not in any model.
this is the error:
FatalErrorException in Post.php line 73:
syntax error, unexpected '(', expecting ',' or ';'
and this is line 73:
protected $user = \Auth::user();

You cannot set a class property's default value like that. Below you can find an excerpt taken from the PHP: Properties Documentation:
This declaration may include an initialization, but this initialization must be a constant value--that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated.
Since Auth::user() depends on run-time information stored in the session for its value to be returned, your definition is invalid. To overcome that you can set the default value in the constructor like so:
protected $user;
public function __construct()
{
$this->user = \Auth::user();
}

Related

How can i get value from config file and set in $attribute variable in laravel model file?

I want to assign a default value in Laravel model but the value should come from the config file.
I am writing below code but its giving me error
protected $attributes = [
'is_generation' => true,
'price' => config('test.MY_PRICE'),
];
it's showing me an error like Constant expression contains invalid operations
How can I get value from config file and set in $attribute variable in Laravel model file?
You can use the saving model event by adding this code to your Eloquent Model:
protected static function boot()
{
// sets default values on saving
static::saving(function ($model) {
$model->price = $model->price ?? config('test.MY_PRICE');
});
parent::boot();
}
With this code in place, if the field price is null, it will have assigned a value from the config key just a moment before saving the Model in the database.
BTW you can change the check like if it's an empty string or less then a number and so on, this is only an example.
Class member variables are called "properties". You may also see them
referred to using other terms such as "attributes" or "fields", but
for the purposes of this reference we will use "properties". They are
defined by using one of the keywords public, protected, or private,
followed by a normal variable declaration. This declaration may
include an initialization, but this initialization must be a constant
value--that is, it must be able to be evaluated at compile time and
must not depend on run-time information in order to be evaluated.
The only way you can make this work is :-
<?php
namespace App;
class Amazon
{
protected $serviceURL;
public function __construct()
{
$this->serviceURL = config('api.amazon.service_url');
}
}
You can use attribute mutator as explained here: https://laravel.com/docs/5.8/eloquent-mutators#defining-a-mutator
Class Example{
public function setPriceAttribute(){
return $this->attributes['price'] = config('test.MY_PRICE');
}
}

Laravel: Function name must be a string

I'm currently working on a project. It was all working just fine until i tried to migrate some tables that I edited. I got this error:
[Symfony\Component\Debug\Exception\FatalThrowableError]
Function name must be a string
Since I doesn't directly show me where the error is, I couldn't find it. Last things I changed before I tried to migrate tables:
Migrations
Laravel colletive/html forms
Store method in my controller
As I know, migrations and forms shouldn't be a problem with this error, so here's my controller code:
public function store(Request $request)
{
$user = Auth::user();
$input = $request->all();
if ($file = $request->file('photo_id')){
$name = time().$file->getClientOriginalName();
$file->move('images', $name);
$photo = Photo::create(['file'=>$name]);
$input['photo_id'] = $photo->id;
}
$user->posts()->create($input);
return redirect('/userPanel');
}
If the error isn't even in a controller code, where could it be. Any help appreciated.
I have found out it was because of a very silly mistake, i was calling a variable as a function...
$ownedMacs = intval($data()['owned_mac']);
Changed to
$ownedMacs = intval($data['owned_mac']);
This error message usually appears when we are doing something real stupid! :)
Same issue in Laravel can solve
Symfony \ Component \ Debug \ Exception \ FatalThrowableError (E_ERROR)
Function name must be a string
When you checked on storage/logs/laravel.log
local.ERROR: Function name must be a string {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\FatalThrowableError(code: 0): Function name must be a string at E:\\Project\\workspace\\turttyKidsProject\\app\\Http\\Controllers\\SiteContactController.php:52)
[stacktrace]
Solution
Data field requesting another format but you are trying to save an object.
$objectSave= new ObjectClass;
$objectSave->feild_db = $request->post('request_name');
Check whether you access request with your form submit method. it may be POST or GET

Laravel - How do I inject automatically in Validation Rule Construct?

I'm not able to inject these variables through Laravel:
//...class AllowedUsername implements Rule...
public function __construct(Router $router, Filesystem $files, Repository $config)
{
$this->router = $router;
$this->files = $files;
$this->config = $config;
}
I get the error:
Type error: Too few arguments to function ... 0 passed in.
Why is Laravel not doing it automatically?
$request->validate([
'username' => ['required', new AllowedUsername],
]);
In order to leverage Laravel's injection magic you need to use Laravel's API which essentially is:
resolve($class) which is wrapper around app($class)
app($class, $params = []) which is wrapper around:
Note: I've changed $abstract for $class
if (is_null($class)) {
return Container::getInstance();
}
return Container::getInstance()->make($class, $parameters);
Classes that you want to resolve out of container (as seen in your code sample):
public function __construct(Router $router, Filesystem $files, Repository $config)
can be resolved only because Laravel maintainers already defined binding for Router::class, Filesystem:class (example: FilesystemServiceProvider).
Repository::class seems to be simple class that does not require parameters (or require parameters that container already knows how to resolve) while "newing up" - thus Laravel can resolve it without problem.
There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection.
Thats why resolve(AllowedUser::class) or resolve(Router::class)... work.
In order to let Laravel know what constructor's parameters should be sent during "newing up" you use bindings mentioned in documentation.

count(): Parameter must be an array or an object that implements Countable

I'm facing strange case. I face an error in production env not while in dev it's working fine.
Development:
Laravel 5.4.28
PHP 7.0.13
MYSQL 5.7.17
Production:
Laravel 5.4.28
PHP 7.2.1
MYSQL 5.7.20
In implementation code. I used:
namespace App;
use Illuminate\Support\Facades\Storage;
use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;
class Artwork extends Model
{
use Searchable;
In development it works fine. But in production it gives me this error:
count(): Parameter must be an array or an object that implements Countable
in Builder.php (line 936)
as you can see in this pic:
Any idea what is the reason behind this? and how to fix?
Put this code at the beginning of your route file, it will work fine
if(version_compare(PHP_VERSION, '7.2.0', '>=')) {
error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
}
This is a documented change in PHP 7.2. You need to either update Laravel to 5.6 or downgrade PHP to version 7.1.
Replace
$originalWhereCount = count($query->wheres);
by
$originalWhereCount = count((array)$query->wheres);
in
\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php
I was facing similar issue in Laravel 5.6. Where I was getting error for object based array. I knew that data in that particular variable will always remain object so i used to convert the object to array. Here is code sample:
$objectData = (array)$objectData;
echo "Total Elements in array are: ".count($objectData);
My server was on PHP 7.1 when I updated to PHP 7.2 I got the same issue.
After searching I found why this occurs. (This occurs because of a PHP update.).
so in my case, the error is solved by typecasting.
I just update all code where I used to count
Before
//this is before
count($adminDetails)
After updated
//after update all i typecast all the code where i used count
count((array)$adminDetails)
Goodluck
This error occurs because you are using a higher PHP version and your Laravel application is on an older PHP version.
✅ Simple solution:
Open: app/Providers/AppServiceProvider.php
And in: public function register() { ... } function add following code:
if(version_compare(PHP_VERSION, '7.2.0', '>=')) {
error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
}
In php 7.2+ count does not work on relation objects, you need to use:
$model->relation()->exists()
Not this (less than php 7.2):
count($model->relation)
i ran into the same problem (PHP 7.2 + Laravel 5.3) but i don't see any "good" answers here. For me, the problem occurs when i tried to start a Builder from a scope method on the model: SomeModel::forUser() calls scopeForUser(). Trying to build a new Query, it trips on a count($this->wheres) that gets no initial value (null). Because the magic static call to the scope starts the builder, no other conditions have been placed in the object so the property is still null at that point.
i thought it's worth sharing my solution first, then perspective on why i consider it better than Ben's answer. It's not personal, i just disagree.
Solution
i took a cue from this answer about overriding some of the core Illuminate\Database classes...
Extend Illuminate\Database\Eloquent\Model
Mine is App\Overrides\Database\Eloquent\Model
Extend Illuminate\Database\Eloquent\Builder
Mine is App\Overrides\Database\Eloquent\Builder
Extend Illuminate\Database\Query\Builder
Can you guess? App\Overrides\Database\Query\Builder
Tell Laravel to use YOUR Eloquent\Model:
config/app.php 'aliases' array, replace the 'Eloquent' value
with your Eloquent\Model FQN
My Model:
namespace App\Overrides\Database\Eloquent;
/*
* Notes:
* * Using replacement Query\Builder with ALIAS
* * Use of Builder in this class is MY Eloquent\Builder
*/
use App\Overrides\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Eloquent\Model as EloquentModel;
class Model extends EloquentModel
{
public function newEloquentBuilder($query)
{
return new Builder($query);
}
protected function newBaseQueryBuilder()
{
$conn = $this->getConnection();
$grammar = $conn->getQueryGrammar();
return new QueryBuilder($conn, $grammar, $conn->getPostProcessor());
}
}
My Eloquent\Builder:
namespace App\Overrides\Database\Eloquent;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
class Builder extends EloquentBuilder
{
public function __construct($query)
{
parent::__construct($query);
/*
* FIX #1: Set properties treated AS arrays
* to empty arrays on construct.
*/
$this->wheres = [];
// Any other properties treated as arrays should also be initialized.
}
}
My Query\Builder:
namespace App\Overrides\Database\Query;
use Illuminate\Database\Query\Builder as QueryBuilder;
class Builder extends QueryBuilder
{
public function __construct()
{
parent::__construct(...func_get_args());
/*
* FIX #2: Set properties treated AS arrays
* to empty arrays on construct.
*/
$this->wheres = [];
// Any other properties treated as arrays should also be initialized.
}
}
This safely preserves the framework's functionality, since the only actual change you're making is initializing properties that should have been in the first place. Everything else will pass instanceof checks used for dynamic loading and dependency injection.
Opinion
While i agree with #ben-harold about every comment he made saying "NEVER edit vendor code," i disagree with the "solution." It's an oversimplification to a much more complex problem.
Upgrade Laravel: to ensure support for PHP 7.2, jumping up several minor versions - if not major releases - is impractical for a lot of teams. As a long term objective, yes of course. As something i can do to get rid of the bug for my deadline? Nope. Upgrading takes a lot of planning and frequently a lot of rewrites as structures, names, and functionality change. It's something to prioritize, but not a need-it-now answer.
Downgrade PHP: same problem. Downgrading into PHP 5.x means A) PHP is EOL, which may be a deal breaker for a lot of customers who have security policies, and B) any usage of PHP 7.x language features have to be scrapped. As with upgrading the framework this is very likely to cause a lot of headaches. It's also an even less useful solution, since walking backward in the language just puts you farther behind and will require more long-term effort.
place the below line ob code before the class name in your controllers
if (version_compare(PHP_VERSION, '7.2.0', '>=')) {
// Ignores notices and reports all other kinds... and warnings
error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
// error_reporting(E_ALL ^ E_WARNING); // Maybe this is enough
}
I was facing the same issue with an external created table (Not using migration or command),
After creating the model, I just assigned a table name, but the problem was in my model protected $fillable where I assign string instead of array and error occurred.
There is 2 possible solution for that.
Assign an array to your protected $fillable = ['filed1', 'filed2'];
Remove protected $fillable completely (Not Recommended)
class Abc extends Model
{
protected $table = 'cities';
protected $fillable = ['field1','field2', ...];
}
Model looking for countable parameter:
class ClassName extend Model {
protected $fillable=['column_name']; // column in DB of Model is in array
}
Before
count($collection['colors'])
Error:Expected type 'Countable|array'. Found 'string'
After
count((array)$collection['colors'])
It works for me!
'vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php' to:
$originalWhereCount = is_array($query->wheres) ? count($query->wheres) : 0;
I;m using laravel 6.x
for this case you can use this way:
$id = \DB::table('xxxx')->where('id', $id)->count();
I Solve this in Laravel 5.6
// in controller
public function index()
{
$todos = Todo::all();
return view('todos.index')->with(['todos' => $todos]);
}
// in view page
#if(count($todos) > 0)
#foreach($todos as $todo)
<div class="well">
<h3>{{$todo->text}}</h3>
<span class="label label-danger">{{$todo->due}}</span>
</div>
#endforeach
#endif

Global Variables in CodeIgniter not Working

I want to generate global variables in CodeIgniter by creating my own library and config file. This is what I wrote ini my library file, let's say globalvars.php. I put it in /application/libraries.
class Globalvars{
function __construct($config = array())
{
foreach ($config as $key => $value) {
$data[$key] = $value;
}
$CI =& get_instance();
$CI->load->library('session');
$CI->load->vars($data);
}
}
I want the user id stored in the session to be available in global variable, so I wrote this in my config file. It's named also globalvars.php. It's in /application/config directory.
$config['user']=$this->session->userdata('id');
I then test to see if it's working by write it in my controller this way.
echo $data['user'];
But I get this error in the browser
Message: Undefined property: CI_Loader::$session
Filename: config/globalvars.php
It seems that the session functions is not defined yet. How can I get it work? What have I missed here? Any help would be appreciated.
You cannot use the session library in config file.
The config files are loaded before any libraries, so $this->session is undefined.
The config.php has to be loaded in order for the Session class to even be initialized, as it reads settings from that file.
A lot of issues with this type of thing (setting some "global" data) can be resolved using a base controller, and extending it in your controllers.
// core/MY_Controller.php
MY_Controller extends CI_Controller {
function __construct()
{
parent::__construct(); // Now the Session class should be loaded
// set config items here
}
}
"Normal" controllers will now extend MY_Controller to take advantage of this.
See: http://codeigniter.com/user_guide/general/core_classes.html for more details.
In addition, when you load->vars(), they are available to the view layer only, it does not create a global variable called $data as you seem to be trying to access. If you do this:
$this->load->vars(array('user' => '1'));
You would access it in a file loaded by $this->load->view() like this:
echo $user; // outputs "1"
See: http://codeigniter.com/user_guide/libraries/loader.html
$this->load->vars($array)
This function takes an associative array as input and generates
variables using the PHP extract function. This function produces the
same result as using the second parameter of the $this->load->view()
function above. The reason you might want to use this function
independently is if you would like to set some global variables in the
constructor of your controller and have them become available in any
view file loaded from any function. You can have multiple calls to
this function. The data get cached and merged into one array for
conversion to variables.
I will say that as an experienced Codeigniter user, the concept of a "global vars" class is a bit wonky and probably unnecessary, especially when it's already so easy to get and set config items. You could definitely run into some confusing issues and variable name conflicts with this method (pre-loading lots of view variables on every request).

Resources