When overriding the base Laravel model create method, the application fails. No errors are sent back to the browser, and the server logs are empty. The strange thing, it works just fine without the override. Not sure what I'm doing wrong.
Simplified controller function:
public function save(Request $request, $id = 0) {
$myModel = MyModel::find($id);
$data = $request->all();
if (!$myModel) // Create a new row
{
$myModel = MyModel::create($data);
}
// ...
}
This works fine until I add this function to the model:
class MyModel extends Model
{
// ...
public static function create($attributes = [])
{
return parent::create($attributes);
}
// ...
}
Just to clarify, I'm not looking for a different way to implement this. I just need to know why the parent call is failing for me.
Here's some server info...
Server OS: Windows 7 (personal PC with XAMPP)
Laravel version 5.4.15
PHP version 5.6.30
The explanation is that the public static function create() is not defined in the Illuminate\Database\Eloquent\Model class.
Is handled as dynamic method call, that is is handled by calling the function:
public static function __callStatic($method, $parameters)
In the Illuminate\Database\Eloquent\Model class.
At the end the create() function is defined in the Illuminate\Database\Query\Builder class.
That is the reason you cant override it in your Model and call parent::create()
IMHO I have not tested entirely, you could try with:
public static function create($attributes)
{
return (new static)->newQuery()->create($attributes);
}
About errors, you should make sure you have enabled log_errors on your server. About the error, when you extend class and want to override method, it should have same signature, so it should be:
public static function create(array $attributes = [])
{
// Your custom code goes here
return parent::create($attributes);
}
(notice array type hinting). Obviously I assume you are going to add something more in this function :)
Related
I have a selection control on a blade form that is to be refreshed via ajax through this function:
function getOpciones(tbName) {
$.get('/ajax/read-data/' + tbName, function(data){
return (data);
});
}
The function takes a string variable 'tbName' whith the name of the table the control is related to, and passes it on as a parameter to the route:
Route::get('/ajax/read-data/{modelo}', 'AjaxController#readData');
Then the controller should get the parameter {modelo}, and retrieve the records in that table:
use App\RegFiscal;
public function readData($modelo) {
$arreglo = $modelo::all();
return response($arreglo);
}
But even though I am referencing the model with 'use App\RegFiscal', all I get is this error in laravel log:
2018-03-23 18:52:08] local.ERROR: exception
'Symfony\Component\Debug\Exception\FatalErrorException' with message
'Class 'RegFiscal' not found' in
C:\wamp64\www\laravel\cte\app\Http\Controllers\AjaxController.php:32
I´m new to Laravel, so needless to say I am lost and any help would be greatly appreciated. Thanks!
Just because you use App\RegFiscal doesn't mean $modelo is associated with it.
What you can do, though, is use app("App\\$modelo") to load in your model based on the parameter you get from the router. You would no longer need to use App\RegFiscal either.
$arreglo = app("App\\$modelo");
return response($arreglo::all());
This is assuming your model is stored in the default app directory within your Laravel project. If not you can change "App\" to where ever it is stored. If for example your model is in app\models\modelname.php it would be "App\Models\\$modelo".
You can do this as the following:
public function readData($modelo) {
$modelName = '\App' . '\\' . $modelo;
$class = new $modelName();
arreglo = $class::all();
return response($arreglo);
}
To those like me who wanted to inject it on a constructor, here's how to do it:
~$ php artisan make:provider MyProvider
Then override the register function like so:
class MyProvider implements ServiceProvider {
/** #override */
public function register() {
$this->app->bind(ShapeInterface::class, function ($app) {
return new Square($app->make(MyModel::class));
});
}
}
The ShapeInterface is a simple interface and Square is a simple class that implements the shape interface with a constructor parameter of the eloquent model.
class Square implements ShapeInterface {
private MyModel $model;
function __construct(MyModel $model) {
$this->model = $model;
}
...
}
I would like to create a question which has many surveys. In the questions Model:
public function surveys()
{
return $this->belongsToMany(Survey::class, 'survey__surveyquestions');
}
And in the controller when saving a new question:
private $questions;
public function __construct(QuestionsRepository $questions)
{
parent::__construct();
$this->questions = $questions;
}
public function store(Request $request)
{
$this->questions->create($request->all());
$this->questions->surveys()->attach($request->surveys);
return redirect()->route('admin.survey.questions.index')
->withSuccess(trans('core::core.messages.resource created', ['name' => trans('survey::questions.title.questions')]));
}
But I get the following error when it gets to the attach line:
(1/1) FatalErrorException Call to undefined method
Modules\Survey\Repositories\Eloquent\EloquentQuestionsRepository::surveys()
I notice the error mentions EloquentQuestionsRepository but I have added no methods in there so it's just an empty class:
class EloquentQuestionsRepository extends EloquentBaseRepository implements QuestionsRepository
{
}
QuestionRepository:
interface QuestionsRepository extends BaseRepository
{
}
As explained in the response to the main post - the constructor resolves the QuestionsRepository to instance of EloquentQuestionsRepository, which by the look of it is not what the store method needs.
What I would probably do is to make call to create method directly on the model and remove constructor all together - that is unless you need the instance of QuestionsRepository anywhere else in your controller:
public function store(Request $request)
{
$question = Question::create($request->all());
$question->surveys()->attach($request->surveys);
...
}
Also - I'm not sure passing $request->all() is the best thing to do - I'd probably use $request->only(...) or $request->all(...) specifying which items you want to get from the request rather than passing everything from the request to the create method.
On the other note - you could also use Form Request, which would validate data for your before passing it to the store method.
https://laravel.com/docs/5.5/validation#form-request-validation
So here's the code
use App\Video;
class HomeController extends Controller
{
protected $video;
public function index()
{
// $video_to_watch is fetched from db and I want to save it and use it in
// another function in this controller
$this -> video = $video_to_watch;
return view('home', compact('video_to_watch'));
}
public function feedback(Request $request)
{
dd($this -> video);
}
}
feedback returns null for some reason.
when I put the
dd($this -> video);
in index() it works fine, not null.
I have tried what's suggested here: Laravel doesn't remember class variables
but it didn't help.
I'm sure it's something stupid I'm overlooking. But can't seem to figure out what, any help much appreciated.
You can't keep your $video value between 2 different requests. You have to fetch your video data in each request.
use App\Video;
class HomeController extends Controller
{
public function index() {
$myVideo = $this->getMyVideo();
return view('home', $myVideo);
}
public function feedback(Request $request) {
dd($this->getMyVideo);
}
private function getMyVideo() {
// fetch $video_to_watch from db
return $video_to_watch ;
}
}
First of all don't fetch data inside a Controller. It's only 'a glue' between model and view. Repeat. No fetching inside a controller.
Use domain services and dependency injection to get business data and if you want to share this data create shared service (single instance).
-
Putting a data object into a controller property class makes a temporary dependency between method calls. Avoid it. Use services instead.
In Laravel, I have a class that I would like to make available to the service controller, make some changes to in the controller action, and then render out with a ViewComposer.
I have done this several times before without issue, but for some reason this time my usual approach is not working - clearly I'm doing something different, and I'm beginning to suspect I've fundamentally misunderstood an aspect of what I am doing.
I have a ServiceProvider with this register() method:
public function register()
{
$this->app->singleton(HelperTest::class, function ($app) {
$pb = new HelperTest();
$pb->test = "jokes on you batman";
return $pb;
});
}
Then in my controller I'm doing the following:
private $helper;
public function __construct(HelperTest $pb)
{
$this->helper = $pb;
$this->helper->test = "hahah";
}
And then I have a viewcomposer doing the following:
private $helper;
public function __construct(HelperTest $pb)
{
$this->helper = $pb;
}
public function compose(View $view)
{
$view->with('output', $this->helper->test);
}
When I call {{ $output }} in the blade view, I expect to see hahah, but instead I get jokes on you batman.
My debugging has shown that all three of these methods are definitely being called. It looks to me like the ViewComposer is for some reason instantiating its own, fresh instance of the class. What am I doing wrong?
Thanks!
Execute php artisan optimize on your console, this will generate an optimized class loader for your application, then check if you can find your class HelperTest registered in services.php inside boostrap/cache. Until HelperTest is not registered there, Laravel IoC can't resolve your class.
I am using a repository pattern and am trying to establish relationships between models. When I try to run the store() method (in the controller) which is trying to use the user() method (which establishes the relationship with the Party model), I get the following error message:
Non-static method Party::user() should not be called statically, assuming $this from incompatible context
I don't understand why I get this error when I try to run the user() relationship method, but all of the other methods (including $this->party->all(), $this->party->create($data)), work just fine.
Here is the relevant code:
// PartiesController.php
public function __construct(Party $party){
$this->party = $party
}
public function store(){
$data = Input::all();
$user = Sentry::getUser();
$this->party->user()->create($data);
}
// Party.php
class Party extends Eloquent{
public function user(){
return $this->belongsTo('User');
}
}
// User.php
use Cartalyst\Sentry\Users\Eloquent\User as SentryUserModel;
class User extends SentryUserModel implements UserInterface, RemindableInterface {
public function party(){
return $this->hasMany('Party');
}
}
// PartyRepository.php
namespace repositories\Party;
interface PartyRepository{
public function all();
public function findByID($id);
public function create($input);
public function user();
}
// EloquentPartyRepository.php
namespace repositories\Party;
use Party;
class EloquentPartyRepository implements PartyRepository{
public function all(){
return Party::all();
}
public function create($input){
return Party::create($input);
}
public function user(){
return Party::user();
}
}
The issue is because you are calling a non-static method in a static context. You may be used to seeing the way Laravel does a lot of this (e.g. User::find() and the like). These, in reality though, are not static calls (a class instance is actually being resolved behind the scenes and the find() method invoked on that instance).
In your case, it is just a plain static method call. PHP would allow this, except for the fact that in the method you are referencing $this and PHP doesn't know what to do with it. Static method calls, by definition, have no knowledge of any instances of a class.
My advice would be to inject an instance of your Model class into your repository's constructor, something like this:
//Class: EloquentPartyRepository
public function __construct(Party $party)
{
$this->party = $party;
}
public function user($partyId)
{
return $this->party->find($partyId)->user();
}
The Party instance you send to the constructor should not be a record from the database, just an empty instance of Party (i.e. new Party()), though I believe if you just add it to the constructor, the IoC should be able to leverage dependency injection and provide you with an instance.
An equivalent implementation is here, that adds a byId method:
//Class: EloquentPartyRepository
public function __construct(Party $party)
{
$this->party = $party;
}
public function byId($partyId)
{
return $this->party->find($partyId);
}
public function user($partyId)
{
if($party = $this->byId($partyId)) {
return $party->user();
}
return null;
}
I have solved the problem. Thank you #watcher and #deczo for your feedback. Both were very helpful and relevant to this error message.
In the end, I only needed to change one line. I had the sequence of method calls out of order in the store() function. Here is the relevant code.
// PartiesController.php
public function store(){
$data = Input::all();
$user = Sentry::getUser();
$user->party()->create($data);
}
In my case, to remove the non-static error and to properly insert the User model into the Party model, I only had to make the aforementioned change.
I referred to http://laravel.com/docs/eloquent/#inserting-related-models for the appropriate sequence.