I am building a shoping cart in Yii. Now I want to buid a function with name is afterCharged() the same as afterSave(). So how to can do it? Thank you in advance
As Michiel said you should use an event in your method
function charge(){
//Perform charge related actions
$this->onCharge(new CEvent($this));
//... maybe some code over here after raising the event
}
public function onCharge($event){
$this->raiseEvent('onCharge', $event);
}
Now you have to catch the raised event. So in this component you'll declare which class and which mehod need to catch it (for example in the init method)
//OtherClass need to catch the the charge event
$callback = array(new OtherClass,'afterCharge')
$component->onCharge = $callback;
So the method afterCharge of OtherClass will be called when charge() is executed.
By the way, the method afterCharge must have the following signature
function afterCharge($event) {
}
Sources:
The wiki
Yii docs
Related
I'm trying to do a twitter android application. I'm still working on the login.
So I'm using asynctaskloader after a friend suggested me to use it. I believe I get a null pointer exception at this line:
this.consumer = (OAuthConsumer) new getCommonsHttpOAuthConsumer(context);
here's my asynctaskloader class:
class getCommonsHttpOAuthConsumer extends AsyncTaskLoader{
public getCommonsHttpOAuthConsumer(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
#Override
public OAuthConsumer loadInBackground() {
// TODO Auto-generated method stub
return new CommonsHttpOAuthConsumer(Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET);
}
}
what am I doing wrong? do you guys need to see more code?
thank you.
You're not using the loader class right.
You need to call the LoaderManager with this line:
getLoaderManager().initLoader(ID_FOR_THIS_LOADER, DATA_BUNDLE, CALLBACK);
If you're in a Fragment you need to add getActivity() at the beginning, and if you are using the android.support.v4.jar, you will call getSupportLoaderManager().
You place this line in your onCreate or onResume method. It will simply notify your activity that you want to start a new loader.
After that you'll need to implement the callbacks notifying that your loader is created/finished. This callbacks are implemented by the object you specified as third parameter (CALLBACK). It can be an activity, a fragment... You will find the syntax online.
Here is what it will look like:
// Callback called by your Activity
#Override
public Loader<OAuthConsumer> onCreateLoader(int id, Bundle arg1) {
loader = new getCommonsHttpOAuthConsumer();
return loader;
// After this method you're going in loadInBackground()
}
#Override
public void onLoadFinished(Loader<OAuthConsumer> loader, OAuthConsumer pl) {
// After loadInBackground() you arrive here, with your new object OAuthConsumer
this.consumer = pl;
}
It should work like this, hope it helps!
What I'm trying to achieve is creating an application that is highly modular. I'm trying to create a cron script which addresses all the cron scripts that need to be fired in all the sub-modules. What I would actually would like to do, is create an event, say runCron that gets fired from a CController and then hook into that when it get's raised from within the sub modules with onRunCron methods. Outlining what I'm trying to do:
Application
|- CronController.php - Raise the event 'RunCron', not knowing which modules will fire after this.
|- Modules
|- Nodes
|- Observe the event 'onRunCron' and run own cron script
|- Users
|- Observe the event 'onRunCron' and run own cron script
What I think I will need to do according to Yii's event system, is create the event and raise it at an application-level (that is still what I'm trying to do) but then; I also need to assign the callbacks from the submodules on an application-level in the controller. Which I do not want, since when a submodule gets added / deleted, the application needs to be adjusted, which does not sound modular at all.
Could someone line out the basics of setting up a event like this and making it as modular as possible? Since I think I'm looking at this completely the wrong way.
Edit (solution):
Thanks to acorncom's answer I've managed to work out the following system.
application.components.CronSingleton
<?php
class CronSingleton extends CApplicationComponent {
/**
* Make sure we "touch" the modules, so they are initialised and are able to attach their listeners.
*/
public function touchModules () {
$modules = Yii::app()->getModules();
if (!empty($modules)) {
foreach ($modules as $name => $module) {
Yii::app()->getModule($name);
}
}
}
/**
* This method should be run to run the cron. It will commense all the procedures in cronjobs
*/
public function execCron($caller) {
$this->touchModules();
$this->onStartCron(new CEvent($caller));
$this->onRunCron(new CEvent($caller));
$this->onExitCron(new CEvent($caller));
}
/**
* Raise an event when starting cron, all modules should add their listeners to this event if they would like to fire something before running the cron.
*/
public function onStartCron ($event) {
$this->raiseEvent('onStartCron', $event);
}
/**
* Raise an event when running cron, all modules should add their listeners to this event to execute during cron run. Mosty this event should be used.
*/
public function onRunCron ($event) {
$this->raiseEvent('onRunCron', $event);
}
/**
* Raise an event when cron exits, all modules should add their listeners to this event when cron exits.
*/
public function onExitCron ($event) {
$this->raiseEvent('onExitCron', $event);
}
}
?>
application.controllers.CronController
<?php
class CronController extends Controller
{
public $layout='//layouts/bare';
public function actionIndex($k) {
Yii::app()->cron->onStartCron = array($this, 'startcron');
Yii::app()->cron->onRunCron = array($this, 'runcron');
Yii::app()->cron->onExitCron = array($this, 'exitcron');
Yii::app()->cron->execCron($this);
}
public function startcron(){
var_dump('CronController - Starting cron');
}
public function runcron(){
var_dump('CronController - Run cron');
}
public function exitcron(){
var_dump('CronController - Ending cron');
}
}
?>
application.modules.admin.AdminModule
<?php
class AdminModule extends CWebModule
{
public function init()
{
// this method is called when the module is being created
// you may place code here to customize the module or the application
// import the module-level models and components
$this->setImport(array(
'admin.models.*',
'admin.components.*',
));
Yii::app()->cron->onRunCron = array($this, 'runCron');
}
public function runCron($event) {
var_dump('AdminModule - Run cron');
}
public function beforeControllerAction($controller, $action)
{
if(parent::beforeControllerAction($controller, $action))
{
// this method is called before any module controller action is performed
// you may place customized code here
return true;
}
else
return false;
}
}
?>
This "proof of concept" setup manages to print out the following result, exactly what I wanted it to do:
string(30) "CronController - Starting cron"
string(25) "CronController - Run cron"
string(22) "AdminModule - Run cron"
string(28) "CronController - Ending cron"
I think you'll want to do something like the following (note: this hasn't been tested, but it should work conceptually).
Create a CApplicationComponent for your cron system. Having it be an application component (registered in your config/main.php file) makes it accessible from anywhere in your app / modules / sub modules / controllers, etc
Have your cron component handle the registration of events / firing of events. Refer to the Yii events page for more info on the details of how this works. Note: you can create your own custom events that subclass the main CEvent class if you need to pass in additional parameters about your events.
Have your modules register as event handlers with your cron component as they initialize.
Have your controller fire off an event to your cron component.
One potential gotcha. I'm not sure whether modules or components are registered first (I believe components should be, but it's worth testing). If your cron component is loading after the modules that are trying to register events with your cron component, then you may want to preload your cron component. There are a few other hacks you can try if that doesn't work (come back and ask for more details).
Oh, and let us know how it goes!
Have you checked the wikis on yii's website about events? I think it's a good place to start, then if you still have some questions we could help you!
Events explained
Behaviors & events
The problem: I'm crashing when I want to render my incoming data which was retrieved asynchronously.
The app starts and displays some dialog boxes using XAML. Once the user fills in their data and clicks the login button, the XAML class has in instance of a worker class that does the HTTP stuff for me (asynchronously using IXMLHTTPRequest2). When the app has successfully logged in to the web server, my .then() block fires and I make a callback to my main xaml class to do some rendering of the assets.
I am always getting crashes in the delegate though (the main XAML class), which leads me to believe that I cannot use this approach (pure virtual class and callbacks) to update my UI. I think I am inadvertently trying to do something illegal from an incorrect thread which is a byproduct of the async calls.
Is there a better or different way that I should be notifying the main XAML class that it is time for it to update it's UI? I am coming from an iOS world where I could use NotificationCenter.
Now, I saw that Microsoft has it's own Delegate type of thing here: http://msdn.microsoft.com/en-us/library/windows/apps/hh755798.aspx
Do you think that if I used this approach instead of my own callbacks that it would no longer crash?
Let me know if you need more clarification or what not.
Here is the jist of the code:
public interface class ISmileServiceEvents
{
public: // required methods
virtual void UpdateUI(bool isValid) abstract;
};
// In main XAML.cpp which inherits from an ISmileServiceEvents
void buttonClick(...){
_myUser->LoginAndGetAssets(txtEmail->Text, txtPass->Password);
}
void UpdateUI(String^ data) // implements ISmileServiceEvents
{
// This is where I would render my assets if I could.
// Cannot legally do much here. Always crashes.
// Follow the rest of the code to get here.
}
// In MyUser.cpp
void LoginAndGetAssets(String^ email, String^ password){
Uri^ uri = ref new URI(MY_SERVER + "login.json");
String^ inJSON = "some json input data here"; // serialized email and password with other data
// make the HTTP request to login, then notify XAML that it has data to render.
_myService->HTTPPostAsync(uri, json).then([](String^ outputJson){
String^ assets = MyParser::Parse(outputJSON);
// The Login has returned and we have our json output data
if(_delegate)
{
_delegate->UpdateUI(assets);
}
});
}
// In MyService.cpp
task<String^> MyService::HTTPPostAsync(Uri^ uri, String^ json)
{
return _httpRequest.PostAsync(uri,
json->Data(),
_cancellationTokenSource.get_token()).then([this](task<std::wstring> response)
{
try
{
if(_httpRequest.GetStatusCode() != 200) SM_LOG_WARNING("Status code=", _httpRequest.GetStatusCode());
String^ j = ref new String(response.get().c_str());
return j;
}
catch (Exception^ ex) .......;
return ref new String(L"");
}, task_continuation_context::use_current());
}
Edit: BTW, the error I get when I go to update the UI is:
"An invalid parameter was passed to a function that considers invalid parameters fatal."
In this case I am just trying to execute in my callback is
txtBox->Text = data;
It appears you are updating the UI thread from the wrong context. You can use task_continuation_context::use_arbitrary() to allow you to update the UI. See the "Controlling the Execution Thread" example in this document (the discussion of marshaling is at the bottom).
So, it turns out that when you have a continuation, if you don't specify a context after the lambda function, that it defaults to use_arbitrary(). This is in contradiction to what I learned in an MS video.
However by adding use_currrent() to all of the .then blocks that have anything to do with the GUI, my error goes away and everything is able to render properly.
My GUI calls a service which generates some tasks and then calls to an HTTP class that does asynchronous stuff too. Way back in the HTTP classes I use use_arbitrary() so that it can run on secondary threads. This works fine. Just be sure to use use_current() on anything that has to do with the GUI.
Now that you have my answer, if you look at the original code you will see that it already contains use_current(). This is true, but I left out a wrapping function for simplicity of the example. That is where I needed to add use_current().
I have a component with a custom model (extending the wicket standard Model class). My model loads the data from a database/web service when Wicket calls getObject().
This lookup can fail for several reasons. I'd like to handle this error by displaying a nice message on the web page with the component. What is the best way to do that?
public class MyCustomModel extends Model {
#Override
public String getObject() {
try {
return Order.lookupOrderDataFromRemoteService();
} catch (Exception e) {
logger.error("Failed silently...");
// How do I propagate this to the component/page?
}
return null;
}
Note that the error happens inside the Model which is decoupled from the components.
Handling an exception that happens in the model's getObject() is tricky, since by this time we are usually deep in the response phase of the whole request cycle, and it is too late to change the component hierarchy. So the only place to handle the exception is very much non-local, not anywhere near your component or model, but in the RequestCycle.
There is a way around that though. We use a combination of a Behavior and an IRequestCycleListener to deal with this:
IRequestCycleListener#onException allows you to examine any exception that was thrown during the request. If you return an IRequestHandler from this method, that handler will be run and rendered instead of whatever else was going on beforehand.
We use this on its own to catch generic stuff like Hibernate's StaleObjectException to redirect the user to a generic "someone else modified your object" page. If you
For more specific cases we add a RuntimeExceptionHandler behavior:
public abstract class RuntimeExceptionHandler extends Behavior {
public abstract IRequestHandler handleRuntimeException(Component component, Exception ex);
}
In IRequestCycleListener we walk through the current page's component tree to see whether any component has an instance of RuntimeExceptionHandler. If we find one, we call its handleRuntimeException method, and if it returns an IRequestHandler that's the one we will use. This way you can have the actual handling of the error local to your page.
Example:
public MyPage() {
...
this.add(new RuntimeExceptionHandler() {
#Override public IRequestHandler handleRuntimeException(Component component, Exception ex) {
if (ex instanceof MySpecialException) {
// just an example, you really can do anything you want here.
// show a feedback message...
MyPage.this.error("something went wrong");
// then hide the affected component(s) so the error doesn't happen again...
myComponentWithErrorInModel.setVisible(false); // ...
// ...then finally just re-render this page:
return new RenderPageRequestHandler(new PageProvider(MyPage.this));
} else {
return null;
}
}
});
}
Note: This is not something shipped with Wicket, we rolled our own. We simply combined the IRequestCycleListener and Behavior features of Wicket to come up with this.
Your model could implement IComponentAssignedModel, thus being able to get hold on the owning component.
But I wonder how often are you able to reuse MyCustomModel?
I know that some devs advocate creating standalone model implementations (often in separate packages). While there are general cases where this is useful (e.g. FeedbackMessagesModel), in my experience its easier to just create inner classes which are component specific.
Being the main issue here that Models are by design decoupled from the component hierarchy, you could implement a component-aware Model that will report all errors against a specific component.
Remember to make sure it implements Detachable so that the related Component will be detached.
If the Model will perform an expensive operation, you might be interested in using LoadableDetachableModel instead (take into account that Model.getObject() might be called multiple times).
public class MyComponentAwareModel extends LoadableDetachableModel {
private Component comp;
public MyComponentAwareModel(Component comp) {
this.comp = comp;
}
protected Object load() {
try {
return Order.lookupOrderDataFromRemoteService();
} catch (Exception e) {
logger.error("Failed silently...");
comp.error("This is an error message");
}
return null;
}
protected void onDetach(){
comp.detach();
}
}
It might also be worth to take a try at Session.get().error()) instead.
I would add a FeedbackPanel to the page and call error("some description") in the catch clause.
You might want to simply return null in getObject, and add logic to the controller class to display a message if getObject returns null.
If you need custom messages for different fail reasons, you could add a property like String errorMessage; to the model which is set when catching the Exception in getObject - so your controller class can do something like this
if(model.getObject == null) {
add(new Label("label",model.getErrorMessage()));
} else {
/* display your model object*/
}
I'm trying to figure out how can I use DispatcherHelperftom MVVM light toolkit in SL, but I can't find any example.
From home page of this framework I know that
DispatcherHelper class, a lightweight class helping you to create
multithreaded applications.
But I don't know how to use it.
How and for what I can use it?
You only need the DispatcherHelper when yo want to make changes to components on your UI thread, from code that runs on a different thread. E.g. in an Silverlight application you call a web service to retrieve some data asynchroneously, and now want to inform the Ui that the data is present via a OnNotifyPropertyChanged event.
First you have to initialize the DispatcherHelper. In Silverlight you do this in Application_Startup:
//initialize Dispatch helper
private void Application_Startup( object sender, StartupEventArgs e) {
RootVisual = new MainPage();
DispatcherHelper.Initialize();
}
In WPF the initialization is done in the static constructor of you App class:
static App() {
DispatcherHelper.Initialize();
}
Then in your event, handling the completion of your asnc call, use the following code to call RaisePropertyChanged on the UI thread:
DispatcherHelper.CheckBeginInvokeOnUI(
() => RaisePropertyChanged(PowerStatePropertyName)
);
DispatcherHelper.BeginInvokeOnUl expects an Action so you can use any code in here just use
DispatcherHelper.CheckBeginInvokeOnUI(
() => { /* complex code goes in here */ }
);
to do more complex tasks.