I try in Zendframework 2 to call a method in model form layout to show some user specific things. I have tried to do it in Module.php in init and onBootstrap and tried to declare some variables that will be available in layout.phtml, but I failed and have not found anything usefull.
You'd typically use a view helper as a proxy to your model for this
Create a view helper in your application, eg.,
<?php
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
class MyModelHelper extends AbstractHelper
{
protected $model;
public function __construct($model)
{
$this->model = $model;
}
public function myCoolModelMethod()
{
return $this->model->method();
}
}
You then make it available by registering it with the framework in your Module.php file using the getViewHelperConfig() method and an anomyous function as a factory to compose your helper, and inject the model it's expecting
<?php
namespace Application;
class Module
{
public function getViewHelperConfig()
{
return array(
'factories' => array(
'myModelHelper' => function($sm) {
// either create a new instance of your model
$model = new \FQCN\To\Model();
// or, if your model is in the servicemanager, fetch it from there
//$model = $sm->getServiceLocator()->get('ModelService')
// create a new instance of your helper, injecting the model it uses
$helper = new \Application\View\Helper\MyModelHelper($model);
return $helper;
},
),
);
}
}
Finally, in your view (any view), you can call your helper, which in turn calls your models methods
// view.phtml
<?php echo $this->myModelHelper()->myCoolModelMethod(); ?>
Related
I am trying to add data to a layout variable via a controller constructor. The reason I want to do this is because I always need to add categories to the topmenu when this controller is called.
No success so far. I add data to a layout via a view composer like this.
namespace App\Http\ViewComposers;
use Illuminate\View\View;
use App\Menu;
class MenuComposer
{
public function compose(View $view)
{
if (in_array($view->getName(), ['layouts.master', 'layouts.master-post', 'layouts.error']))
{
$menu = Menu::menu('topmenu');
view()->with('topmenu', $menu);
// view()->share('topmenu', $menu); not working either
}
}
}
I want to extend the data in a Controller constructor.
namespace App\Http\Controllers\Post;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\View\View;
class PostController extends Controller {
public function __construct(View $view)
{
$view->offsetGet('topmenu');
// $view->gatherData() not working either
}
Whatever I try, Laravel throws an exception:
Target [Illuminate\Contracts\View\Engine] is not instantiable while building [App\Http\Controllers\Post\PostController, Illuminate\View\View].
What I did in the serviceprovider boot function:
view()->share('topmenu', [
'items' => $newItemsToAdd
]);
In the viewComposer I did:
$extraItems = view()->shared('topmenu');
if (!empty($extraItems)) {
$items = aray_merge($items, $extraItems);
}
}
I have a general class for uploads.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class File extends Model
{
protected $guarded = ['id'];
}
An ID of File model will be specified on each person to serve as the avatar:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Person extends Model
{
public function avatar()
{
return $this->belongsTo('App\File');
}
public function putAvatar($file)
{
$path = $file->store('avatars');
// This would work if `avatar()` was a `hasMany()` relation
$this->avatar()->create([
'path' => $path,
]);
}
}
This doesn't work exactly as intended, but it creates the File model in database. Why?
The $this->avatar() is an instance of BelongsTo and there is no create method. I checked the class, the included traits and the Relation class that it extends. Reference here.
So what's going on, where is the code that creates the new model?
I tried using a ReflectionMethod but while $this->avatar()->create() works, new ReflectionMethod($this->avatar(), 'create') returns a ReflectionException with message Method Illuminate\Database\Eloquent\Relations\BelongsTo::create() does not exist.
There is no method for saving entities on belongsTo relationships. Once the entity is created, you can associate it with the model.
$avatar = File::create([...]);
$this->avatar()->associate($avatar)->save();
To allow querying of relationships, undefined method calls are passed to an Eloquent Builder instance which does have a create method.
All relationships extend the Relation class which defines:
public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
$result = $this->query->{$method}(...$parameters);
if ($result === $this->query) {
return $this;
}
return $result;
}
The method used for belongsTo() should be save(), not created(). Make sure to pass as an argument your File class:
$this->avatar()->save(new File([
'path' => $path,
]));
I'm trying to provide a way to track when a user makes a change to a model for a notes section in my application. E.g. John goes and modifies 2 fields, a note would be created saying John has changed title from 'My title 1' to 'My title 2' and content from 'Lipsum' to 'Lipsum2'.
Here is a trait I created:
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Model;
trait TrackChanges
{
public $changes;
public static function bootChangesTrait()
{
static::updating(function($model)
{
$this->changes = [];
foreach($model->getDirty() as $key => $value)
{
$original = $model->getOriginal($key);
$this->changes[$key] = [
'old' => $original,
'new' => $value,
];
}
});
}
}
And I am using that trait successfully on my model. However, I'm not sure how to capture the contents of the changes, or if they are even working correctly.
In my controller I have:
$site = Site::findOrFail($id);
// Catch and cast the is_active checkbox if it's been unselected
if ( ! $request->exists('is_active') )
{
$request->request->add([ 'is_active' => 0 ]);
}
// // Get rid of the CSRF field & method
$data = $request->except([ '_token', '_method' ]);
$site->update($data);
I tried dd($site->changes) before and after $site->update($data); but it just returns null.
What am I doing wrong?
You need to change your boot method in your trait to bootTrackChanges(). To boot traits you need to follow the naming pattern of boot{TraitName} for your boot method. Then you need to change your $this calls in your trait to $model so the change get saved to the model so your trait should look like this:
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Model;
trait TrackChanges
{
public $changes;
public static function bootTrackChanges()
{
static::updating(function($model)
{
$changes = [];
foreach($model->getDirty() as $key => $value)
{
$original = $model->getOriginal($key);
$changes[$key] = [
'old' => $original,
'new' => $value,
];
}
$model->changes = $changes;
});
}
}
Another thing to note is if you have defined a boot method in your model make sure you call the parent boot method as well or else your trait's boot methods will not be called and your listener will not be registered.. I have spent hours and hours on this one before due to forgetting to call the parent method. In your model defining a boot method is not required but if you did call the parent like:
class MyModel extends Model
{
use TrackChanges;
protected static function boot()
{
// Your boot logic here
parent::boot();
}
}
I have a model config which has the following at the top:
<?php
namespace App;
use DB;
use Illuminate\Database\Eloquent\Model;
class Config extends Model
{
protected $table = 'config';
public function getConfigVariables()
{
$config = DB::table('config')->where('is', '1')->first();
session()->put('name',$config['name']);
session()->put('infoemail',$config['infoemail']);
session()->put('copyrightowner',$config['copyrightowner']);
and I wish to call this in a controller to set up the session so in the route for the top level I set set up
Route::get('/',
[
'uses' => 'ConfigController#ConfigVariables',
'as' => 'home'
]);
The config controller method which does not work is:
public function ConfigVariables()
{
Config::getConfigVariables();
session()->put('thisyear',ReturnCurrentYear());
$footer = "© ".session()->get('thisyear').", ".session()->get('name');
session()->put('footer',$footer);
return view('welcome');
}
but this does not work and I am stuck!
Change
public function getConfigVariables()
to
public static function getConfigVariables()
You might want to read up how object oriented works, basically when you do Config::getConfigVariables(); you are trying to call a static method, without instantiating the class.
A good start would be here, the concept applies everywhere.
I'm working with the M2e extension for Magento. Now I want to call a method of the class Ess_M2ePro_Adminhtml_ListingController in the file app/code/community/Ess/M2ePro/controllers/Adminhtml/ListingController.php.
But I don't know, how. I can't create an object or model to get access to the class to use the methods. Maybe it's not a good idea to call this controller methods directly, but in my case (remove a associated magento product to an ebay listing) it's required to call this methods.
In general these actions are called from the magento backend. I've also tried to create an admin_html session, but at the moment I don't have any further ideas.
Here's an example, how it looks like. I'm working with regular PHP-code, nothing special:
class Ess_M2ePro_Adminhtml_ListingController extends Ess_M2ePro_Controller_Adminhtml_MainController
{
//#############################################
protected function _initAction()
{
/** removed **/
}
protected function _isAllowed()
{
return Mage::getSingleton('admin/session')->isAllowed('m2epro/listings/listing');
}
//#############################################
public function indexAction()
{
/** removed **/
}
//#############################################
public function searchAction()
{
/** removed **/
}
public function searchGridAction()
{
/** removed **/
}
public function lockListingNowAction()
{
$listingId = (int)$this->getRequest()->getParam('id');
$component = $this->getRequest()->getParam('component');
$lockItemParams = array(
'id' => $listingId,
'component' => $component
);
$lockItem = Mage::getModel('M2ePro/Listing_LockItem',$lockItemParams);
if (!$lockItem->isExist()) {
$lockItem->create();
}
exit();
}
}
And I'm looking for something like this:
$test = Mage::getModel('M2ePro/Ess_M2ePro_Adminhtml_ListingController')->lockListingNowAction();
You shouldn't call methods from an other controller. Specially in your case, when you have exit at the end of the method.
You can use the _forward method if you are in a controller:
$this->_forward($action = 'lockListingNowAction', $controller = 'adminhtml_listing', $module = 'M2ePro', $params = array('id'=>$id)) //controller name may be different
But the cleanest way is to have the code you need in a helper and call the code from that helper in both controllers.