Note: I had read tons of information here and another sources, including official docs.
I have a payment extension - ex title simplepay.
I want to know specifically if it is a way to "listen" to a system (predefined) event.
I want to run some logic when an order status has changed.
In the admin/controller/extension/payment/simplepay.php I have this (nothing more elsewhere):
public function install()
{
$this->load->model('setting/event');
/** addEvent($code, $trigger, $action, $status = 1, $sort_order = 0); */
$this->model_setting_event->addEvent(
'do_transaction_on_order_status_change',
'catalog/controller/api/order/history/after',
'extension/payment/simplepay/doTransactionOnOrderStatusChange');
}
public function uninstall()
{
$this->load->model('setting/event');
/** deleteEventByCode($code); */
$this->model_setting_event->deleteEventByCode('do_transaction_on_order_status_change');
}
public function doTransactionOnOrderStatusChange(&$route, &$data)
{
// testing purpose for the moment
$log = new Log('aaaaaa.log');
$log->write('Route ' . $route);
}
The event do_transaction_on_order_status_change is properly registered in events list.
What I am doing wrong?
Nevermind!
After a while I got the point.
My method doTransactionOnOrderStatusChange(&$route, &$data) was writted with 3 parameters.
Like doTransactionOnOrderStatusChange(&$route, &$data, &$output).
The problem is that OC 3+ not accept a third parameter, even if there is a "before" or an "after" event.
And another problem was the related event: it must be admin/controller/sale/order/history/before. (or /after)
No other event on order change worked. (probably this event is the only admin event, the rest being from catalog).
Later edit
The above works only for trigger the method, but had nonsense to do what was supposed to.
So, after other research time, it was obvious that the event I need to listen to was: catalog/controller/api/order/history/after. (an event raised from admin, but from catalog, weird!!).
To listen to that event, was need to make another controller in catalog/controller/extension/payment/simplepay.php then put that method (doTransactionOnOrderStatusChange) inside it.
Note that my question contains the proper event.
I hope someone will find this helpful!
Related
I am developing an TYPO3 extbase extension to connect to my external application that has a REST-API. All I want is to retrieve the data from my REST-API and pass this data to the controller. Since I am quite new to extbase development, I didn't really find any resources about Repository interacting with a webservice. Only documentation about Repository that interacts with a database (MySQL, PostgreSQL, ..)
I would like to know, where should I place the cURL-Request Function to connect to the API? In the Model? In the Repository? How can the Controller in my Extension access that data? From Model or Repository?
What would be the best practice for retrieving the data from my external application/database ? (the data retrieved from the application is JSON-formatted)
thanks for any advice / help!
What we usually do is create a Service (in Classes/Service) and use that to connect to the webservice and fetch the data. If you want Models, you can create them there as well.
However, now that I think about it, technically it should be a Repository. It shouldn't matter where the Repository gets its data. Extbase shouldn't have any problem with a completely custom Repository (not extending any other class).
Lets take a simple list -> detail view and make it as simple as possible in order to get some results. Then you can add some helper classes and include them via namespaces etc.
ListAction (yourExtension/Classes/Controller/EventController)
/**
* action list
*
* #return void
*/
public function listAction()
{
$events = (some function which gets all the available events and results to a json output)
$decodedEvents = json_decode($events , true);
(in case you need to see what you got back)
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($decodedEvents);
$this->view->assign('events', $decodedEvents);
}
List HTML (yourExtension/Resources/Private/Templates/Event/List)
<f:for each="{events}" as="event">
<h2>{event.title}</h2>
</for>
Now if i am not wrong, every event has a uid or something that identifies it. So on the same template you can do the following:
<f:for each="{events}" as="event">
<h2>{event.title}</h2>
<f:link.action action="detail" arguments="{eventId: event.uid}">More</f:link.action>
</f:for>
What this does, is to generate a link which links to your detail action with an extra parameter, the eventId.
Bedore TYPO3 renders the detail page it will go through your detail action to get the information for this specific event in order to display them.
DetailAction (yourExtension/Classes/Controller/EventController)
/**
* action detail
*
* #return void
*/
public function detailAction()
{
$args = $this-request->getArguments();
$eventId = $args['eventId'];
$getEvent = (some function that gets a specific id and results to a json output)
$decodedEvent = json_decode($getEvent , true);
(in case you need to see what you got back)
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($decodedEvent );
$this->view->assign('event', $decodedEvent );
}
What this does, it to retrieve the argument that we specified in order to get the identifier of the event and include it on your api request to get the event back. Then we just just decode it to a normal array and we send the results to the FrontEnd.
Detail HTML (yourExtension/Resources/Private/Templates/Event/Detail)
<h2>{event.title}</h2>
Thats a simple list -> detail proccess on TYPO3. You will oft see that the detail view as show but is basically the same thing.
You can replace the variables with a dummy json values and play around. I tested the code while i was writing this answer and it works.
Classes/Service
As for the Classes/Service that mentioned by Rudy Gnodde, you can put your library in there (assuming that you already have coded some functions like getEvent, getAllEvents, getPersons etc.) and then call them in the controller.
use \Vendor\YourExtension\Service\RestApiClass;
/**
* action list
*
* #return void
*/
public function listAction()
{
$events = RestApiClass::getAllEvents();
$decodedEvents = json_decode($events , true);
(in case you need to see what you got back)
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($decodedEvents);
$this->view->assign('events', $decodedEvents);
}
/**
* action detail
*
* #return void
*/
public function detailAction()
{
$args = $this-request->getArguments();
$eventId = $args['eventId'];
$getEvent = RestApiClass::getEvents($eventId);
$decodedEvent = json_decode($getEvent , true);
(in case you need to see what you got back)
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($decodedEvent );
$this->view->assign('event', $decodedEvent );
}
If you need more information feel free to ask.
Best regards
I am creating a new ember app. I want to use the newest version of ember-data. (ember-data 2.0). I want it to be a mobile webapp. Therefore it must handle variable network access and even offline.
I want it to store all data locally and use that data when it goes offline so the user gets the same experience regardless of the network connectivity.
Is ember-data 2.0 capable of handling the offline case? Do I just make an adapter that detects offline/online and then do....?
Or do I have to make my own in-between layer to hide the offline handling from ember-data?
Are there any libraries out there that has this problem solved? I have found some, but are there any that is up to date with the latest version of ember-data?
If device will go offline and user will try to transition to route, for which model is not loaded yet, you will have an error. You need to handle these situations yourself. For example, you may create a nice page with error message and a refresh button. To do this, you need:
First, In application route, create error action (it will catch errors during model hook), and when error occurs, save transition in memory. Do not try to use local storage for this task, it will save only properties, while we need an actual transition object. Use either window.failedTransition or inject in controllers and routes a simple object, which will contain a failed transition.
actions: {
error: function (error, transition) {
transition.abort();
/**
* You need to correct this line, as you don't have memoryStorage
* injected. Use window.failedTransition, or create a simple
* storage, Iy's up to you.
*/
this.get('memoryStorage').set('failedTransition', transition);
return true; //This line is important, or the whole idea will not work
}
}
Second, Create an error controller and template. In error controller define an action, retry:
actions: {
retry: function () {
/**
* Correct this line too
*/
var transition = this.get('memoryStorage').getAndRemove('failedTransition');
if (transition !== undefined) {
transition.retry();
}
}
}
Finally, In error template display a status and an error text (if any available) and a button with that action to retry a transition.
This is a simple solution for simple case (device gone offline just for few seconds), maybe you will need something way more complex. If you want your application to fully work without a network access, than you may want to use local storage (there is an addon https://github.com/funkensturm/ember-local-storage) for all data and sync it with server from time to time (i.e sync data every 10 sec in background). Unfortunately I didn't try such things, but I think it is possible.
I have set up some basic routing in my app by using the examples at http://emberjs.com/guides/outlets/#toc_the-router
Within the root I have some events that trigger from view actions e.g:
gotoStepOne: Ember.Route.transitionTo('stepOne'),
gotoStepTwo: Ember.Route.transitionTo('stepTwo'),
gotoStepThree: Ember.Route.transitionTo('stepThree'),
gotoStepFour: Ember.Route.transitionTo('stepFour'),
gotoStepFive: Ember.Route.transitionTo('stepFive'),
Full example router code at http://jsfiddle.net/hellosmithy/WdjXT/
This all works fine at the moment. The problem is that I'd like to add other code into these events. For example:
gotoStepOne: function() {
if (someCondition) {
Ember.Route.transitionTo('stepOne');
}
someOtherFunction();
}
However doing this breaks the routing without throwing any errors. It just no longer transitions.
Specifically I only want transitions to happen if a certain state is met - something has been selected or input by the user at each stage before they can proceed. Is there a workaround for this, or should I be abstracting this functionality elsewhere?
The way I understand the router is, that it is the representation of the application's state.
Specifically I only want transitions to happen if a certain state is met - something has been selected or input by the user at each stage before they can proceed.
So the user inputting or selecting something puts your application in a certain state which is reflected by the router.
IMHO it should be something like this in a view (or controller):
userDidSomething: function(condition) {
if (condition) {
App.get('router').send('stepOne');
}else{
someOtherFunction();
}
}
i'm studying this source base. Basically this is an Anim server client for Symbian 3rd edition for the purpose of grabbing input events without consuming them in a reliable way.
If you spot this line of the server, here it is basically setting the RProperty value (apparently to an increasing counter); it seems no actual processing of the input is done.
inside this client line, the client is supposed to be receiving the notification data, but it only calls Attach.
my understanding is that Attach is only required to be called once, but is not clear in the client what event is triggered every time the server sets the RProperty
How (and where) is the client supposed to access the RProperty value?
After Attaching the client will somewhere Subscribe to the property where it passes a TRequestStatus reference. The server will signal the request status property via the kernel when the asynchronous event has happened (in your case the property was changed). If your example source code is implemented in the right way, you will find an active object (AO; CActive derived class) hanging around and the iStatus of this AO will be passed to the RProperty API. In this case the RunL function of the AO will be called when the property has been changed.
It is essential in Symbian to understand the active object framework and quite few people do it actually. Unfortunately I did not find a really good description online (they are explained quite well in Symbian OS Internals book) but this page at least gives you a quick example.
Example
In the ConstructL of your CMyActive subclass of CActive:
CKeyEventsClient* iClient;
RProperty iProperty;
// ...
void CMyActive::ConstructL()
{
RProcess myProcess;
TSecureId propertyCategory = myProcess.SecureId();
// avoid interference with other properties by defining the category
// as a secure ID of your process (perhaps it's the only allowed value)
TUint propertyKey = 1; // whatever you want
iClient = CKeyEventsClient::NewL(propertyCategory, propertyKey, ...);
iClient->OpenNotificationPropertyL(&iProperty);
// ...
CActiveScheduler::Add(this);
iProperty.Subscribe(iStatus);
SetActive();
}
Your RunL will be called when the property has been changed:
void CMyActive::RunL()
{
if (iStatus.Int() != KErrCancel) User::LeaveIfError(iStatus.Int());
// forward the error to RunError
// "To ensure that the subscriber does not miss updates, it should
// re-issue a subscription request before retrieving the current value
// and acting on it." (from docs)
iProperty.Subscribe(iStatus);
TInt value; // this type is passed to RProperty::Define() in the client
TInt err = iProperty.Get(value);
if (err != KErrNotFound) User::LeaveIfError(err);
SetActive();
}
Using doctrine 2.1 (and zend framework 1.11, not that it matters for this matter), how can I do post persist and post update actions, that involves re-saving to the db?
For example, creating a unique token based on the just generated primary key' id, or generating a thumbnail for an uploaded image (which actually doesn't require re-saving to the db, but still) ?
EDIT - let's explain, shall we ?
The above is actually a question regarding two scenarios. Both scenarios relate to the following state:
Let's say I have a User entity. When the object is flushed after it has been marked to be persisted, it'll have the normal auto-generated id of mysql - meaning running numbers normally beginning at 1, 2, 3, etc..
Each user can upload an image - which he will be able to use in the application - which will have a record in the db as well. So I have another entity called Image. Each Image entity also has an auto-generated id - same methodology as the user id.
Now - here is the scenarios:
When a user uploads an image, I want to generate a thumbnail for that image right after it is saved to the db. This should happen for every new or updated image.
Since we're trying to stay smart, I don't want the code to generate the thumbnail to be written like this:
$image = new Image();
...
$entityManager->persist($image);
$entityManager->flush();
callToFunctionThatGeneratesThumbnailOnImage($image);
but rather I want it to occur automatically on the persisting of the object (well, flush of the persisted object), like the prePersist or preUpdate methods.
Since the user uploaded an image, he get's a link to it. It will probably look something like: http://www.mysite.com/showImage?id=[IMAGEID].
This allows anyone to just change the imageid in this link, and see other user's images.
So in order to prevent such a thing, I want to generate a unique token for every image. Since it doesn't really need to be sophisticated, I thought about using the md5 value of the image id, with some salt.
But for that, I need to have the id of that image - which I'll only have after flushing the persisted object - then generate the md5, and then saving it again to the db.
Understand that the links for the images are supposed to be publicly accessible so I can't just allow an authenticated user to view them by some kind of permission rules.
You probably know already about Doctrine events. What you could do:
Use the postPersist event handler. That one occurs after the DB insert, so the auto generated ids are available.
The EventManager class can help you with this:
class MyEventListener
{
public function postPersist(LifecycleEventArgs $eventArgs)
{
// in a listener you have the entity instance and the
// EntityManager available via the event arguments
$entity = $eventArgs->getEntity();
$em = $eventArgs->getEntityManager();
if ($entity instanceof User) {
// do some stuff
}
}
}
$eventManager = $em->getEventManager():
$eventManager->addEventListener(Events::postPersist, new MyEventListener());
Be sure to check e. g. if the User already has an Image, otherwise if you call flush in the event listener, you might be caught in an endless loop.
Of course you could also make your User class aware of that image creation operation with an inline postPersist eventHandler and add #HasLifecycleCallbacks in your mapping and then always flush at the end of the request e. g. in a shutdown function, but in my opinion this kind of stuff belongs in a separate listener. YMMV.
If you need the entity id before flushing, just after creating the object, another approach is to generate the ids for the entities within your application, e. g. using uuids.
Now you can do something like:
class Entity {
public function __construct()
{
$this->id = uuid_create();
}
}
Now you have an id already set when you just do:
$e = new Entity();
And you only need to call EntityManager::flush at the end of the request
In the end, I listened to #Arms who commented on the question.
I started using a service layer for doing such things.
So now, I have a method in the service layer which creates the Image entity. After it calls the persist and flush, it calls the method that generates the thumbnail.
The Service Layer pattern is a good solution for such things.