Validate Payment Method in Magento 2 - validation

I want to check whether a customer is allowed to use one of the activated payment methods.
For example:
If the customer had enabled "Cash on Delivery", he should see the method but I can't find anything similar in the code.
In Magento 1 there was:
public function isAvailable()
Is there any similar way to do it with Magento 2 ?

There are 2 approaches you could take:
Observer
If you take a look at \Magento\Payment\Model\Method\AbstractMethod (the parent class of \Magento\OfflinePayments\Model\Cashondelivery) in the isAvailable method, you will find that it dispatches the payment_method_is_active event which could be used to performs some additional logic (such as determine if a customer had enabled the specific method or not):
$this->eventManager->dispatch(
'payment_method_is_active',
[
'result' => $checkResult,
'method_instance' => $this,
'quote' => $quote
]
);
Plugin
Since isAvailable is a public method, you could add a plugin to the method. Whether you use before, after, or around depends on your specific needs. However, a good place for learning how to do this is the Magento DevDocs site: http://devdocs.magento.com/guides/v2.0/extension-dev-guide/plugins.html

Related

How to add / remove elements from array that is in Request

My request looks like this
Array
(
[name] => Eugene A
[address] => Array
(
[billing] => Array
(
[address] => aaa
)
[shipping] => Array
(
[address] => bbb
)
)
)
I need to delete the shipping address. But how?
I can only delete both addresses,
$request->request->remove('address');
but I don't want it.
I want to delete only shipping address, like so
$request->request->remove('address.shipping');
But it is not working for me
Laravel 5.6
Update
Why do I need it?
Easy. I have abstracted out my Form Request validation into a class that is a child to Illuminate\Foundation\Http\FormRequest.
I actually have few classes for validation. I call them one by one in a controller like so:
app()->make(CustomerPostRequest::class); // validate Customer information
app()->make(AddressSaveRequest::class); // validate Addresses
Why?
Now I can Mock this requests in unit-tests, and I can have my validation abstracted out. And I can use Address validation in many places.
But Now I need more flexibility. Why?
Because AddressSaveRequest rule looks like this
public function rules(): array
{
return [
'address.*.address' => [
'bail',
'required',
'string',
],
...
It validates all addresses.
But sometimes I don't want to validate shipping address, if the the chech_box - ship_to_the_same_address is ticked.
But I have my Address validator abstracted in separate file and it is used in many places. There are places where ship_to_the_same_address tick box is not presented.
Thus I cannot use 'required_unless:ship_to_same_address,yes',
And I cannot use
app()->makeWith(AddressSaveRequest::class, ['ship_to_the_same_address ' => 'yes']);
Because Taylor said ...when calling makeWith. In my opinion it should make a new instance each time this method is called because the given parameter array is dynamic.. And it does, and it does not work correctly with app()->instance(AddressSaveRequest::class, $addressSaveRequest); and cannot be mocked in unit tests.
Why Taylor decided it - I seriously don't know.
PS
And yes, I know that mocking requests is not recommended.
If you were trying to add or remove inputs from the Request itself:
You can add data to the request pretty easily by merging it in and letting Laravel handle which data source is being used:
$request->merge(['input' => 'value']);
That will merge in the input named input into the input source for the Request.
For removing inputs you could try to replace all the inputs without that particular input in the replacement:
$request->replace($request->except('address.shipping'));
Just one idea to try.
Try this:
$request->except(['address.shipping']);
Details: Laravel Request
Laravel has a helper method called array_forget, which does exactly what it sounds like:
$requestArray = $request->all();
$newArray = array_forget($requestArray, 'address.shipping')
Documentation
After the edit to the main question with why some inputs of the request are to be deleted, my main answer isn't correct anymore. User Lagbox has the correct answer for the question that was asked.
However, I would like to note that another solution would be to have seperate Request classes with validation. One for placing an order (assuming it is a system where someone can order stuff) where ship_to_same_address is present and another one for things like updating your account, like PlaceOrderRequest and UpdateAccountRequest classes.

Magento adding actions In orders

I'm using Magento Community 1.7. When I go to Sales-->Orders I see all the orders. Currently I can tick certain orders and, using the action dropdown, I can print invoices, cancel, hold etc. I'm wondering if there is anyway that I can add actions like "Requires Refund", "Fraud". The actions wouldn't neccessarily have to do anything just change the status so I can see clearly for later the status of these orders. Any ideas?
In order to add more actions you need to override this method Mage_Adminhtml_Block_Sales_Order_Grid::_prepareMassaction().
For each action you add, you will need a method in the controller you send it to.
Take for example the action Cancel. It is added to the mass action block like this:
$this->getMassactionBlock()->addItem('cancel_order', array(
'label'=> Mage::helper('sales')->__('Cancel'),
'url' => $this->getUrl('*/sales_order/massCancel'),
));
and the corresponding method is Mage_Adminhtml_Sales_OrderController::massCancelAction()

How to set up FubuMVC validation

I'm trying to learn FubuMVC and have gotten stuck on validating my input models.
What I want to accomplish is post-validate-redirect. That is, to redirect to same view and show the errors if the model is invalid. I'm using attributes on my models.
Also, how would I specify my own error messages, i.e localization?
I'm using the latest packages of Fubu from nuget.
My registry looks like this:
IncludeDiagnostics(true);
Applies.ToThisAssembly();
Actions.IncludeClassesSuffixedWithController();
Routes
.HomeIs<HomeController>(x => x.Index())
.IgnoreControllerNamesEntirely()
.IgnoreMethodsNamed("Index")
.IgnoreMethodsNamed("Query")
.IgnoreMethodsNamed("Command")
.IgnoreNamespaceText("Features")
.IgnoreMethodSuffix("Html")
.RootAtAssemblyNamespace()
.ConstrainToHttpMethod(x => x.Method.Name.EndsWith("Command"), "POST")
.ConstrainToHttpMethod(x => x.Method.Name.EndsWith("Query"), "GET");
this.UseSpark();
this.Validation();
HtmlConvention<SampleHtmlConventions>();
Views.TryToAttachWithDefaultConventions();
The FubuMVC.Validation package is really just an example of how to use FubuValidation as we haven't built it out for all of the edge cases. Having said that, let me explain a little bit about how it works so we can see if you can use it, or if you should just handroll your own validation behavior.
The ValidationBehavior uses the IValidationFailureHandler interface to "handle" validation failures. The Notification object built up from FubuValidation is shoved into the IFubuRequest when the behavior fires, and then the handler is called.
The ValidationFailureHandler class is wired up by default for all
validation failures. This delegates to the IValidationFailurePolicy to
determine the strategy to use for a given model type (see my post on
policies for an explanation of how this works).
The Validation extension method has an overload which gives a micro-
dsl for configuring these policies:
this.Validation(x => {
x.Failures....
});
From here you can 1) apply custom policies via the ApplyPolicy method
or 2) use the predicate based configuration approach via the IfModel methods.
If you go the predicate route (e.g., x.Failures.IfModelIs()), you can tell FubuMVC.Validation to use FubuContinuations to redirect or transfer to another behavior
chain. Rex recently posted about FubuContinuations if you're looking for some guidance in this area (http://rexflex.net/2011/07/fubumvc-fubucontinuation/).
Hope this helps and feel free to ask away if I didn't explain anything enough,
Josh

How do I get the shipping method the user has chosen during checkout?

I want to get the name of the shipping method the user has chosen during checkout. Does anyone know how to retrieve that info?
This will get it to some extent but it is cached:
Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress()->getShippingDescription();
When I am on the onestep checkout and I go back to the shipping tab and change the shipping, it is still holding the old shipping method. I need to figure out how to get the current one.
Foreword
Constructed from Magento app/code/core/Mage/Checkout/Block/Onepage/Shipping/Method/Available.php and others:
app/design/frontend/base/default/template/checkout/onepage/shipping_method/available.phtml uses this code to determine which shipping method was selected:
$this->getAddressShippingMethod()
app/code/core/Mage/Checkout/Block/Onepage/Shipping/Method/Available.php expands that code to this:
return $this->getAddress()->getShippingMethod();
Let's research a bit and expand it even deeper:
$this->getQuote()->getShippingAddress()->getShippingMethod();
Parent block expands method getQuote():
return $this->getCheckout()->getQuote();
And deeper:
public function getChechout() {
return Mage::getSingleton('checkout/session');
}
Merging all that code gives us this:
Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress()->getShippingMethod()
That gives you the shipping method code. Giving that, you could manipulate it just as you wish. This data is stored within the database, so when you change shipping method, the code changes too.
Getting deeper and deeper!
If you've ever created your own shipping method, you'd know, that it has the method called collectRates().
It fills a set of shipping/rate_result_method models, stores it within the instance of shipping/rate_result model and returns it (you can get each model' instance using Mage::getModel(<model i've named>); ).
Yet, note: one could contain multiple rate_result_method instances, while the shipping method code is the same for all those instances!
Thus, in order to get the description, you need to get one of the rate_result_method instances and retrieve its methodTitle or carrierTitle.
After a small researching i've found how to retrieve all these rates:
Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress()->getShippingRatesCollection()
This will provide you with a collection of all rates for the selected shipping method. You can operate it with getItems() and get a hash. Or you could use getFirstItem() and use it as the template.
Anyway, let's assume u've retrieved some item of that collection and stored it within the $rate variable:
$rate->getCarrier(); // This will provide you with the carrier code
$rate->getCarrierTitle(); // This will give you the carrier title
$rate->getCode(); // This will give you **current shipping method** code
$rate->getMethod(); // This will provide you with the **shipping method** code
$rate->getMethodTitle(); // This will tell you current shipping method title
$rate->getMethodDescription(); // And this is the description of the current shipping method and **it could be NULL**
That's all, folks!
I am really sorry for my poor English and for my strange mind flow. Hope this will help you or someone else. Thanks!
Just in case you need it still. You can get shipping method from order by:
$order->getShippingMethod();
Of course how you get your $order depends on context.
Also you can get description by:
$order->getShippingDescription();
shipping method in magento
$methods = Mage::getSingleton('shipping/config')->getActiveCarriers();
$options = array();
foreach($methods as $_code => $_method)
{
if(!$_title = Mage::getStoreConfig("carriers/$_code/title"))
$_title = $_code;
$options[] = array('value' => $_code, 'label' => $_title . " ($_code)");
}
echo "<xmp>";
print_r($options);
echo "</xmp>";
In your checkout controller you need to add extra steps to save your quote if you want this information to be accessible to you.
I added a few '$quote->save();' entries to get this to work, however, I cannot definitively say which entry is the one that did the fix. I also cannot find the link on Magento forums, however, I hope I have given you a head start on what is going on.
You could override the saveShippingMethodAction() function in the Mage_Checkout_OnepageController, or extend upon it, and save the method into the registry by inserting:
Mage::register('blahShippingMethod', $this->getRequest()->getPost('shipping_method'));
and call upon it as you need it: Mage::registry('blahShippingMethod');
Don't forget to unset it when you no longer need it as you will run into an error if you try to reset when it's already been set.
Mage::unregister('blahShippingMethod');

MVC Putting an action in the most appropriate correct controller

I was just wondering what the best practice approach is for deciding where to create an action/view in certain situations.
If User hasMany Video
where is the best place to create the action/view to show user videos?
So within the Users account page 'My Videos' link do you
just create a users/my_videos action and view.
create videos/my_videos action and view.
or as is most likely you would already have a Controller/Action of videos/index which would have search functionality. Simply use this passing in a user id.
Any thoughts/advice greatly appreciated
Thanks
Leo
One potential option is to do the following:
Since the videos likely have much more code around them than a simple which user has which videos lookup the video list action should be in the VideosController.
In past projects I have (in CakePHP 1.3) used prefix routing to address some of this.
In config/core.php make sure you enable routing.prefixes to include a 'user' prefix.
<?php
... in routes.php ...
Routing.prefixes = array( 'user' );
?>
In the videos controller make an action with the following signature:
<?php
...
public function user_index( $userID = null ){
...
}
?>
and in the views where you link to the list of users videos the html::link call should look similar to the following:
<?php
...
echo $this->Html->link( 'User\'s Videos', array(
'controller' => 'videos',
'action' => 'index',
'prefix' => 'user',
$this->Session->read( 'Auth.User.id' )
));
?>
Of course this assumes you are using the Auth component here to track the logged in user. The Session helper code to read the authenticated user id might need tweaking.
This lets you a) Not worry too much about routing aside from enabling prefix routing and b) will quickly let you have pretty links like so -- site.com/user/videos/index/419
Couple this with some Slug love ( this is the best link for this I have seen - no slug field required on the db layer - http://42pixels.com/blog/slugs-ugly-bugs-pretty-urls )
You could even end up with urls like so quite easily: site.com/user/videos/index/eben-roux
and with just a tiny bit of editing to app/config/routes.php you could eliminate the /index/ portion and the results would be SEO friendly and user friendly in the format:
site.com/user/videos/eben-roux
http://book.cakephp.org/view/945/Routes-Configuration
As always with code you have the two extremes of:
1) Putting everything in a single controller
2) Having every action in a separate controller
The ideal approach will nearly always be somewhere between the two so how to decide what is grouped together and what is separated?
In MVC I tend to look at the Views and see what the commonalities are: as you point out Users have a ref to a collection of Videos in the Model, but would you want both sets of Data in any single View? i.e. In this example is it likely that you would be on a page that both managed user details, and displayed the list of vids? If not then I'd suggest separate controllers.
If either controller would then be extremely simple - e.g. one method, then may be worth considering merging the two.
I like to keeps things separate.
What I'd do is an index action in videos controller, passing user's id as argument and then displaying only current users video.
public function index($id = null){
$this->paginate = array( 'conditions'=> array('Video.user_id' => $id));
$this->set('videos', $this->paginate());
}
My take is that it depends on the responsibility you assign to the controllers.
I would say that something like a User or a Video controller should be concerned with only those entities.
You may want to consider something like a UserDashboard (or something similar but appropriately named) as alluded to by Dunhamzzz in the comments. This can aggegate all the functionality from an "entry" point-of-view. The same way a banner / shortcut / action menu would work.
Your UserDashboard would use whatever data layer / repository is required to get the relevant data (such as the IVideoRepository or IVideoQuery implementation).
Usually when something doesn't feel right it isn't. Try splitting it out and see how it works. You can alsways re-arrange / refactor again later.
Just a thought.
I don't think there's a 'one-rule-fits-all' solution to this question, but I would try to take an approach in which you would determine what the main object is that you're dealing with, and adding the action/view to that object's controller.
In your example I'd say that your main object is a video and that the action you're requiring is a list of video's filtered by a specific property (in this case the user's id, but this could very well be a category, a location, etc.).
One thing I would not do is let your desired URL determine in which controller you put your functionality. URLs are trivially changed with routes.

Resources