Using Yii's CActiveRecord onBeforeDelete - events

I'm trying to attach some listeners to a CActiveRecord's onBeforeDelete, this I'm doing as follows:
<?php
class SomeModule extends CWebModule
{
public function init()
{
Submission::model()->onBeforeDelete = array($this, 'cleanUpFiles');
}
public function cleanUpFiles ($event) {
var_dump('Well... Hi there, being deleted are we?');
}
}
?>
Sadly, this does not have any effect on Submission's delete(). Though, when replacing onBeforeDelete with onBeforeFind, it seems to react seamlessly on the find methods. I'm having the feeling that the onBeforeDelete should be applied to an instance of Submission and not on it's singleton model, is that feeling correct? If I'm correct, is there any other way to attach my event listener in global on every Submission?
Thanks in advance!

I've managed to solve this with a little bit of improvisation, I've overwritten the Submission's function beforeDelete() as follows:
<?php
protected function beforeDelete() {
foreach (Submission::model()->onBeforeDelete as $key => $value) {
$this->onBeforeDelete = $value;
}
return parent::beforeDelete();
}
?>
Now, when delete is called, the beforeDelete() will rip out the onBeforeDelete from the singleton model and assign it to it's own instance. Now, the callbacks are being triggered.

Related

How to pass data to all views in Laravel 5.6?

I have two controllers. StudentController and TeacherController. I have a variable $chat which I want to pass in all the views of StudentController and TeacherController. The $chat will contain different data for both these controllers.
I searched and found ways, but I am getting empty data. I am doing it like this.
<?php
namespace App\Http\Controllers;
use View;
class StudentController extends Controller {
public function __construct()
{
$this->middleware('auth')->except(['home']);
$this->middleware('access')->except(['home']);
$chats = studentChat();
View::share('chats', $chats);
}
So, here I am printing and it is returning an empty array, but when I use the same in a function the array contains data. What is wrong here? Can anyone please help?
What I tried:
public function boot()
{
View::composer('*', function ($view) {
$chats = Cache::remember('chats', 60, function () {
if(Auth::user()->user_type() == config('constant.student'))
{
return studentChat();
}
else
{
return teacherChat();
}
});
$view->with('chats', $chats);
});
}
If you use View::share your share data to ALL your view, if you need to add data to few different views you may do this:
Create blade file(chat.blade.php for your case), and put your variables:
<? $chats = studentChat(); ?>
Include this file to the begining of your views where your need this 'global' varables:
//begin of your blade file
#include('chat')
//some code
{{ $chat->channel }}
Sharing Data With All Views
Occasionally, you may need to share a piece of data with all views that are rendered by your application. You may do so using the view facade's share method. Typically, you should place calls to share within a service provider's boot method. You are free to add them to the AppServiceProvider or generate a separate service provider to house them:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
$chats = studentChat();
View::share('chats', $chats);
}
public function register()
{
//
}
}
So, what I did was in the AppServiceProvider class, in the boot function I added this.
View::composer('*', function ($view) {
if(!\Auth::check())
{
return;
}
$userType = \Auth::user()->user_type ;
if($userType == config('constant.student'))
{
$chats = studentChat();
}
else if($userType == config('constant.teacher'))
{
$chats = teacherChat();
}
$view->with('chats', $chats);
});
You can pass data to the view Like.
return View::make('demo')->with('posts', $posts);
For more details visit article : Introduction to Routing in Laravel
write your query in boot method in appServiceProvider like,
View::composer('*', function ($view) {
$share_query = Cache::remember('share_query', 60,function () {
return App\User::all();
});
$view->with('share_query', $share_query);
});
Your final solution is ok, but not the cleanest possible.
Here is what i would do.
Define a class with a single function that contains your logic and return $chats, that way you will encapsulate your logic properly and keep your service provider boot method clean.
Then you have 2 options:
Inject your class in the boot() method of the service provider you use, then call its function and uses View::share. Should looks like :
public function boot(ChatResolver $chatResolver)
{
$chats = $chatResolver->getChats();
View::share(compact('chats));
}
If you only use $chats variable in a signe view or partial (like a part of layout), you can also inject the class you defined directly in the view.
Here is a link to Laravel doc regarding that.
In some cases it might be the easiest solution.

laravel5.2 job, how to return data?

Job
class DataFormFields extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
protected $fieldList = [
'name' => 'Tom',
'age' => '20',
];
public function handle()
{
$fields = $this->fieldList;
return $fields;
}
}
controller
public function create()
{
$data = $this->dispatch(new DataFormFields());
return view('create', $data);
}
I try to dd($data); print 0
the code can work in laravel5.1 ,but in 5.2 it's not ok.help
Laravel5.2 the class will implement the Illuminate\Contracts\Queue\ShouldQueue interface, indicating to Laravel that the job should be pushed onto the queue instead of run synchronously.
So you should make job like php artisan make:job fooJob --sync
I don't think you want to rely on the return from dispatch(), as queued jobs can have their execution delayed by an arbitrary amount of time, depending on your implementation. You probably want to consider using Events or a callback.
https://laravel.com/docs/5.2/queues#job-events
Or, at the end of the handle function you could do something like:
call_user_func(['FormFieldsHandler', 'dataFormFieldsCallback'], $data]);
Getting a list of fields like that isn't really the use case for a Job, as the return value of the handle() method is never returned to the calling scope through the dispatch() method.
It seems like something best left to a Service, or a model even.
Here's how you might implement this with a service.
<?php
namespace App\Services;
class DataFieldService
{
protected $fields = ['field_one', 'field_two'];
public function getFields()
{
return $this->fields;
}
}
And in the controller...
<?php
namespace App\Http\Controllers;
use App\Services\DataFieldService;
class MyController
{
// in Laravel, the IoC container will inject DataFieldService
// for you automagically if you type hint it
public function create(DataFieldService $dataFieldService)
{
$fields = $dataFieldService->getFields();
return view('create', compact('fields'));
}
}
And, obviously, you can flesh out the DataFieldService to get the fields from a database or something. Hope that helps!
Yes you can. Simply return the value. Instead of calling dispatch, you can call the job's handle like this.
public function handle()
{
$a = "return value";
return $a;
}
}
Now instead of calling $this->dispatch(new ExampleJob) in your controller. You can do this instead.
$exampleJob = new ExampleJob();
$retval = $exampleJob->handle();
echo $retval; //return value
Please note that only works if you do not intend to queue the job.
You can't get result from async job . when you implements from ShouldQueue interface it means that you tend exec job Async . you must remove ShouldQueue to exec sync job to get result

Call method from (custom) controller class in Magento

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.

Laravel 4 - Model properties' names different than database columns

I have one question, that seems to be logical, but I can't find answer for it.
Let's say I have Model Task:
class Task extends Eloquent {
protected $fillable = array('is_done');
}
So, I have one property is_done, but when working on frontend and backend part of application, I would like to have isDone as model property.
Is there a way to say it to framework, to somehow repack it for me? So that I am able to use isDone, throughout application, and that Model takes care of converting it to is_done, when it comes to saving/updating.
This would help me, so I don't have to think about names specified in database (like when using alias in traditional SQL clauses).
Is this possible at all? Does it make sense?
To prevent writing a getter/setter methods for every single attribute of the model, you can override the magic methods from the Eloquent class to access them in camelCase style:
class Model extends Eloquent {
public function __get($key)
{
$snake_key = snake_case($key);
return parent::__get($snake_key);
}
public function __set($key, $value)
{
$snake_key = snake_case($key);
parent::__set($snake_key, $value);
}
public function __isset($key)
{
$snake_key = snake_case($key);
return parent::__isset($snake_key);
}
public function __unset($key)
{
$snake_key = snake_case($key);
parent::__unset($snake_key);
}
}
Would a getter method for your attribute help you? If yes:
<?php
class Task extends Eloquent {
public function isDone()
{
return $this->getAttribute('is_done');
}
}
If not, and you really need to access $Task->isDone: try to overwrite the $key in magic _get() method for $key == 'isDone' (and maybe other attributes) and return the parent::_get() with $key:
<?php
class Task extends Eloquent {
public function __get($key)
{
if($key == 'isDone')
$key = 'is_done';
return parent::__get($key);
}
}
And perhaps, your Eloquent needs an attribute mapper for the attribute magic methods ;)

Codeigniter models: only return not load? Are models just namespaces?

I'm finding out about how CI scopes things a bit late. I've been creating models like this:
$this->load->model('user');
$this->user->load ($user_id);
Then I'd pass around the $this->user object to be able to access all the various things I needed from that object, update properties and such.
I downloaded a Phil Sturgeon CI app callend PyroCMS and I see that he mostly returns data from his object's methods, much like a straight-up procedural function.
So, are models really only supposed to be used at namespaces in CI?
I'm finding that using them the way I am, with a just-now-discovered scope issue, I'm over-writing my models.
Of course the solution is the name it when loading, but that means I have to track and be wary of what name each one of them is using, which is going to be a problem.
Is this how others use the CI models, mainly returning things from them instead of using them as full featured objects?
I found Phil Sturgeon responded to this question: Codeigniter models are just utility classes? with essentially what I need to know. I can still use the loaded model by using the php $object = new Class syntax. I will do this:
class Companies
{
private $_users;
public function __construct ()
{
$this->load->model ('users');
$this->_users = new Users;
}
}
With the private and the new I think I'm safe finally. Probably I should go ahead and do that outside of the model, and not in the constructor, then pass it in as a dependency. I had given up on DI.
I think I've talked myself off the ledge.
After 2 years with CI, here's how I've begun to use the models:
// Singleton class to lookup Users and perform other
// tasks not related to one specific user
class User_model extends MY_Model {
public static $CI;
public function __construct()
{
parent::__construct();
self::$CI =& get_instance();
}
public function getByEmail($email)
{
$data = $this->db->where('email', $email)->get('users')->first_row();
if ($data)
{
$user = new User;
return $user->load($data);
}
}
public function getAllUsers()
{
$data = $this->db->get('users')->result();
foreach ($data as &$row)
{
$user = new User;
$row = $user->load($row);
}
return $data;
}
//... other functions that makes sense in a singleton class
}
// Actual user object. Instantiate one for every user you load...
class User {
public function __construct($id)
{
$data = User_model::$CI->db->where('id', $id)->first_row();
$this->load($data);
}
public function load($data)
{
foreach ($data as $k => $v)
{
$this->$k = $v;
}
return $this;
}
}

Resources