Controller does not send data to view, arrives as null - php

I've been trying to send data from controller to view in CodeIgniter 4, let me just...
Model:
class KomikModel extends Model
{
protected $table = 'komik';
protected $useTimestamps = true;
protected $allowedFields = ['judul', 'slug', 'penulis', 'penerbit', 'sampul'];
public function getKomik($slug = false)
{
if ($slug == false) {
return $this->findAll();
}
return $this->where(['slug' => $slug])->first();
}
}
Controller:
public function edit($slug)
{
$data = [
'title' => 'Form Ubah Data',
'validation' => \Config\Services::validation(),
'komik' => $this->komikModel->getKomik($slug)
];
return view('komik/edit', $data);
}
View:
<?= dd($komik); ?>
After all that, $komik that arrived in the view comes out as null. Why, what did I do wrong? Thanks in advance.
PS: Somehow, it works fine with other methods. currently there are 3 other methods using the model to send data from controller towards the view. but only at this edit method the problem occurs.

The issue is with this particular line.
return $this->where(['slug' => $slug])->first();
Model Class doesn't have any "where" method defined in it. It borrows it from Builder Class. This is called object composition. You can certainly try to have your own method with a similar implementation as in the Model class but I would recommend against it since it is an antipattern.
A better approach would be to have a single class that is responsible for all the querying or filtering as in Repository Pattern. You can do a bit of research on that.

Related

Is it possible to add data to view data from controller constructor

I have a data that I want to return in all of my controller methods and I thought that'd be cleaner to return from the controller's constructor; because currently it's just repetitive return code.
protected $data;
public function __construct() {
$this->data = "Test";
}
public function index() {
// Stuff
return view('test')->with([
'testData' => $this->data
// other view data
]);
}
public function store() {
// Stuff
return redirect()->back()->with([
'testData' => $this->data
// other view data
]);
}
This is just a pseudo example.
Yes, that's possible. It's done exactly in the same manner you showed.
However, I don't think that's the best way to do it. You might want to take a look into ViewComposers, which help provide a data set to a view (or multiple views) right after the controller but before the view is finally provided to the user.
You could just write a controller method to append the data property for you:
protected function view($name, $data = [])
{
return view($name, $data + $this->data);
}
public function index() {
...
return $this->view('view', ['other' => 'data']);
}
You can use ViewComposer which allow you to attach data to the view every time certain views is rendered
namespace App\ViewComposers;
class DataComposer
{
protected $data = ['1', '2', '3']; // This data is just for sample purpose
public function compose(View $view)
{
$view->with('data', $this->data);
}
}
And to register this composer with the list of all views on which the data must by attached add this code in the boot method off the AppServiceProvider
View::composer(
['view1', 'view2', 'view3', '....'],
'App\ViewComposers\DataComposer'
);
create a after middleware However, this middleware would perform its task after the request is handled by the application
Yes, it is very much possible with the code you have
[EDIT]
If you wish to remove ['testData' => $this->data] from all controller methods, then you can use view composers.
View composers are linked to one view. So for that view, if you have the same set of data to be passed all the time, use view composers!

Reading data using Anahkiasen/Polyglot returns error trying to get property of non-object

After adding the Anahkiasen/Polyglot package to my Laravel 4.2 project I tried to get this to work. I set up everything the way I think it's supposed to be (the documentation is kinda bad). Saving to the database doesn't seem to be a problem but when I want to read I get the following error:
Trying to get property of non-object (View: /Applications/MAMP/htdocs/*my view*.blade.php)
Models:
use Polyglot\Polyglot;
class Page extends Polyglot {
use SoftDeletingTrait;
protected $fillable = [
'lang',
'meta_title',
'meta_description',
'title',
'page_title',
'page_content',
];
protected $polyglot = [
'meta_title',
'meta_description',
'title',
'page_title',
'page_content',
];
// ...
}
class PageLang extends Eloquent {
public $timestamps = false;
protected $fillable = [
'page_id',
'lang',
'meta_title',
'meta_description',
'title',
'page_title',
'page_content',
];
}
My blade template:
$page->nl->title
/*
This is what's causing the error
$page->title doesn't produce errors but is, of course, empty
*/
Been stuck on this for a while now. Any help is greatly appreciated :-)
I'm not familiar with the library, but looking at the base Polyglot class, it looks like this abstraction works by using PHP's magic __get variable to inject n localization object when you access something like $page->nl
Looking at __get,
public function __get($key)
{
// If the relation has been loaded already, return it
if (array_key_exists($key, $this->relations)) {
return $this->relations[$key];
}
// If the model supports the locale, load and return it
if (in_array($key, $this->getAvailable())) {
$relation = $this->hasOne($this->getLangClass())->whereLang($key);
if ($relation->getResults() === null) {
$relation = $this->hasOne($this->getLangClass())->whereLang(Config::get('polyglot::fallback'));
}
return $this->relations[$key] = $relation->getResults();
}
// If the attribute is set to be automatically localized
if ($this->polyglot) {
if (in_array($key, $this->polyglot)) {
/**
* If query executed with join and a property is already there
*/
if (isset($this->attributes[$key])) {
return $this->attributes[$key];
}
$lang = Lang::getLocale();
return $this->$lang ? $this->$lang->$key : null;
}
}
return parent::__get($key);
}
there's a number of conditionals that, if failed, result in the parent's __get being called
return parent::__get($key);
In other words, the normal model behavior. That's probably what's happening above -- and since nl isn't a set object PHP complains when you try to call a method on it.
Of the three conditionals in __get, this seems like the most likely candidate for failure
if (in_array($key, $this->getAvailable())) {
$relation = $this->hasOne($this->getLangClass())->whereLang($key);
if ($relation->getResults() === null) {
$relation = $this->hasOne($this->getLangClass())->whereLang(Config::get('polyglot::fallback'));
}
return $this->relations[$key] = $relation->getResults();
}
If your specific case, it's going to look like
if (in_array('nl', $this->getAvailable())) {
//...
}
Looking at getAvailable()
protected function getAvailable()
{
return Config::get('polyglot::locales');
}
it looks for a configuration field named Config::get('polyglot::locales');. I'd check if calling that configuration field returns nl as a configured locale
var_dump(Config::get('polyglot::locales'));
My guess would be it doesn't, possibly because you didn't run the artisan command to publish the configuration for the package
php artisan config:publish anahkiasen/polyglot

In my view, why does Laravel overwrite variables I declare in my controller with those from my model?

Am I understanding the MVC design pattern incorrectly? Why does Laravel seemingly overwrite variables I declare in my controller and pass to my view with those from my model? Say I'm passing the variable $journey from my controller to my view like so:
class JourneyController extends BaseController {
public function journey($id) {
$journey = Journey::find($id);
// I overwrite one of the attributes from the database here.
$journey->name = "Overwritten by the Controller";
return View::make('journey', array(
'journey' => $journey,
'bodyClass' => 'article'
));
}
}
But, I'm using an accessor to also modify the $journey->name attribute in my Journey model:
class Journey extends Eloquent {
protected $table = 'journeys';
protected $primaryKey = 'id';
public $timestamps = false;
public function getNameAttribute($value) {
return 'Overwritten by the Model';
}
}
So when my view is created, and I display $journey->name like so:
{{ $journey->name }}
I'm left with:
"Overwritten by the Model";
Why does this occur? Doesn't the controller handle a request, fetch information from my model, manipulate it, and then pass it to the view where it can be outputted? If this is the case, why, and also how, is the model seemingly 'jumping' in between to overwrite my controller-written variable with its own?
I know this is old, but I just found a solution on Laravel 4.2 today.
class Journey extends Eloquent {
protected $table = 'journeys';
protected $primaryKey = 'id';
public $timestamps = false;
public function getNameAttribute($value = null) {
if($value)
return $value;
return 'Overwritten by the Model';
}
}
You should update your getNameAttribute function as above to return the set value (if there is one) instead of always returning the string. Previously, calling this value would always run the function and ignore the set value, but now the function takes checks first for the value that you have set.
Hopefully 2 years isn't too late to still help some people!
Have a look at using Presenters, Take Jeffery Way's Presenter Package. Install it normally and then you can add the $presenter variable to your model.
For instance:
use Laracasts\Presenter\PresentableTrait;
class Journey extends Eloquent {
use PresentableTrait;
protected $presenter = "JourneyPresenter";
}
Then you can create your JourneyPresenter Class:
class JourneyPresenter {
public function journeyName()
{
return "Some Presentation Name";
}
}
In your view you can call this, like so:
<h1>Hello, {{ $journey->present()->journeyName }}</h1>
It really helps keep this sort of "presentation" logic out of your view and controller. You should try hard to keep your controller solely for its intended purpose, handling routes and basic guards and keep your views logic-less.
As for your problem, you may just be experiencing the natural order of Laravel operations.

Yii - Get data from Model

I have the following in my controller:
public function actionIndex() {
$userID = Yii::app()->user->getId();
$arNotifs = Notification::model()->getNotificationsByUserId($userID);
$this->render('index', array("arNotifications"=>$arNotifs, "userID"=>$userID));
}
I have the following in a file called notification.php in my models:
class Notification extends CActiveRecord {
// ------ BUNCH OF STUFF
public function getNotificationsByUserId($userId) {
$userId = (int) $userId;
$query = Yii::app()->db->createCommand();
$query->select('n.id, n.title, n.content, n.updated');
$query->from('hr4_notification_x_user nxu');
$query->join('hr4_notification n', 'nxu.notification = n.id');
$query->where('nxu.user=:userId', array(':userId' => $userId);
return $query->queryAll();
}
// ------ MORE STUFF
}
When I rem out the line
$arNotifs = Notification::model()->getNotificationsByUserId($userID);
and replace it with a static value it works fine. It seems that in my noob ways I am missing some vital step. The controller seems to have no idea what Notification is.
Thanks in advance
I believe the most elegant way to get your notifications on the controller would be something like:
$arNotifs = Yii::app()->user->model->notifications;
To achieve such, you might need to implement a getModel() method on your class that extends CWebUser. That method would return an instance of an user that extends CActiveRecord. Then your user model can have a relations() method, like the following:
class UserModel extends CActiveRecord {
public function relations() {
return array(
'notifications' => array(self::HAS_MANY, 'Notification', 'user'),
);
}
}
This will prevent you from writing that query and will make things more clear (on both, models and controller). If you will, read a bit about relations.
You cannot use the Notification model like this.
You can instantiate it with $notification = new Notification();
and then do a $notification->getNotificationsByUserId($userID);
However this would be not very good. I would move the notification code from the model to the User model.
This was you dont even need to pass the user ID.
Or maybe even better, if you make a component out of Notification and use it as a service.

suggestion on restful laravel

I am recently started using laravel, i am using following restful approach, please suggest if this approach is good...
Author controller
class AuthorsController extends BaseController {
public $restful = true;
public function getIndex()
{
return View::make('authors.index')->with('title', 'Showing Authors')->with('authors', Author::all());
}
public function newAuthor()
{
if(Request::isMethod('post')){
$author = Author::create(array(
'name' => Input::get('name'),
'bio' => Input::get('bio')
));
if($author->id) return Redirect::route('authors')->with('message', 'The Author was created successfully!');
}
else{
return View::make('authors.new')->with('title', 'New Author');
}
}
}
Routes
Route::get('authors', array('as' => 'authors', 'uses' => 'AuthorsController#getIndex'));
Route::match(array('GET', 'POST'), 'authors/new', array('as' => 'new_author', 'uses' => 'AuthorsController#newAuthor'));
Please suggest if i am using correct approach for create method, and i am using same method for add form and post request.
thanks.
Your code probalby works, but you could improve it by joining your routes, controllers and migrations in one resource.
With laravel generator package
After installing that package, with the following command:
Route::resource("path","SomeController");
You will get generated resources for you app. That includes list of restful controller actions.
For example you for main GET method you will have generated index action in you controller.
Close. You shouldnt need to test the request method in a controller action if the routes/controller is set up properly.
Author Controller
class AuthorsController extends BaseController {
public function getIndex()
{
return View::make('authors.index')->with('title', 'Showing Authors')->with('authors', Author::all());
}
public function getNew()
{
return View::make('authors.new')->with('title', 'New Author'); //this view should contain a form that POST's to /authors/new
}
public function postNew() {
$author = Author::create(Input::all()); //note: use $fillable on your model here to prevent any extra fields from breaking things
return Redirect::action('AuthorsController#getNew')->with('message', 'The Author was created successfully!');
}
}
Routes
Route::controller("/authors", "AuthorsController")

Categories