I have created new payment method (gateway). In this gateway I sending information to bank for credit payment and I use some additional payment options like Name/Person Age/Person Profit/Credit Term/...
By this fields I calculate Credit Term and send all of this data to bank.
I would like to show this information in Payment Method info block (right sidebar in default theme), but I would not like to save this fields to database (so in admin area later I will have information like it was standart Check/Money Order payment and just payment method name would be another)
I can't show this fields in Payment Method info block, because it shows only fields stored in database and only way that I found - store this data in core/session and then in info block retrieve this data back
I doing something like this in Payment Model:
class OS_LacPayCS_Model_Payment extends Mage_Payment_Model_Method_Abstract
{
...
public function assignData($data)
{
parent::assignData($data);
$session = Mage::getSingleton('core/session');
$session->setData('payment_additional', $data);
return $this;
}
...
}
and then getting it
class OS_LacPayCS_Block_Payment_Info extends Mage_Payment_Block_Info
{
...
public function getPaymentInfo()
{
$session = Mage::getSingleton('core/session');
return $session->getData('payment_additional');
}
...
}
Is there another way to get this data?
And also I wish to add some additional rows in Order Review Tab on checkout, how can I add them w/o rewriting review template and block?
Thanx
Magento payment api defines additional_information field that is saved as serialized array to db and that you can use for storing the data you might need to display. You can set data to there by assignData($data) method
Related
I'm in a dilemma to find some sort of logic to restrict user access to content within the same model.
For example, a supplier only can see products that they supply and the customer only can see a product which they buying. (note: Each product can have multiple supplier or customers. We call id a product node)
Now, I have the relationship set to a product belongs to many suppliers and a product belongs to multiple customers.
Currently I have the spatie roles and permissions in my site, which works great for 1 tenant (mainly our office(50-150 users)). It is not a problem if our office user can see details of multiple customers or products, but the problem starts when the customer logs in. I only want to show the product pricing or data that belonging to them. It is a big no no to see any other customer or supplier data.
I looked multi tenancy implementation, but I believe this wouldn't cover my need.
I apologise if I've overlooked something, but I try to keep the data as secured as possible.
Could you please shed some light on this dilemma and direct me to the correct path?
Many thanks for your input!
I'm assuming that all of the models you want to restrict have a relationship directly to the customer, so you can actually add a global scope that adds a default parameter to the query.
Take the following scope:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Support\Facades\Auth;
class CustomerOwnedScope implements Scope {
public function apply(Builder $builder, Model $model) {
if (Auth::guard('customer')->check()) {
$builder->where('customer_id', '=', Auth::guard('customer')->id);
}
}
public function extend(Builder $builder) {
$this->addWithoutCustomer($builder);
}
protected function addWithoutCustomer(Builder $builder) {
$builder->macro('withoutCustomer', function (Builder $builder) {
return $builder->withoutGlobalScope($this);
});
}
}
Any model that has this scope will automatically add the clause WHERE customer_id = ? where ? is the id of the currently authenticated customer, if one is authenticated. Assuming that you're using Laravel auth this would prevent you from having to do anything specific to achieve your goal.
It also adds the scope withoutCustomer() that would prevent the where clause from being added.
The simplest way to add this to a model that belongs to a customer would be to create yourself a trait (concern) like so:
<?php
namespace App\Concerns;
use App\Scopes\CustomerOwnedScope;
trait OwnedByCustomer {
public static function bootOwnedByCustomer() {
static::addGlobalScope(new CustomerOwnedScope);
}
public function customer() {
$this->belongsTo(Customer::class, 'customer_id');
}
}
This would add the customer relationship as well as add the scope to automatically query based on the current customer.
You can obviously modify this further to include other relationships, or you can add some more conditions to only apply for customers with a certain flag set, or not set (for internal users, etc).
This whole approach does assume that your internal admin users and your external customer users are using different auth guards (which would be the ideal approach in this situation).
I should add that the code above is taken from an article I wrote on the subject of multi-tenancy, specifically the part about dealing with tenants in a single database. If you would like, you can read it here: https://ollieread.com/articles/laravel-multi-tenancy-avoiding-over-engineering#single-database
How to display all user invoices for referencing in the admin section of the application.
I can get a user invoices by
$userinvoices = $user->invoices();
Or I can get all invoice by stripe API:
$invoices = \Stripe\Invoice::all(array("limit" => 30));
in the second case, I can't get the details of the user the invoice belongs to.
Or is there any way to save invoice data to database on every creation of the invoice in stripe.
Your first option is the better way to go since you have all the information on the invoice and also the user info on the object.
If you want, you can go to your stripe dashboard -> Your account -> account settings -> webhooks -> add endpoint -> select events and select the invoiceitem.created event. setup your endpoint in the application and do whatever you need with it.
Example:
public function invoiceCreated(Request $request){
$payload = $request->all();
if($payload['type'] == 'invoiceitem.created'){
// do whatever you want with the $payload["data"]...
}
}
Good Luck :)
I'm rather new to Laravel 4 and can't seem to find the right answer, maybe you can help:
A User in our application can have many Accounts and all data is related to an Account, not a User. The account the User is currently logged into is defined by a subdomain, i.e. accountname.mydomain.com.
We added a method account() to our User model:
/**
* Get the account the user is currently logged in to
*/
public function account()
{
$server = explode('.', Request::server('HTTP_HOST'));
$subdomain = $server[0];
return Account::where('subdomain', $subdomain)->first();
}
The problem is that there is always an extra query when we now use something like this in our view or controller:
Auth::user()->account()->accountname
When we want to get "Products" related to the account, we could use:
$products = Product::where('account_id', Auth::user()->account()->id)->get();
And yet again an extra query...
Somehow we need to extend the Auth::user() object, so that the account data is always in there... or perhaps we could create a new Auth::account() object, and get the data there..
What's the best solution for this?
Thanks in advance
Just set it to a session variable. This way, you can check that session variable before you make the database call to see if you already have it available.
Or instead of using ->get(), you can use ->remember($minutes) where $minutes is the amount of time you wish to keep the results of the query cached.
You should take a look at Eloquent relationships : http://laravel.com/docs/eloquent#relationships
It provides simple ways to get the account of a user and his products. You said that a user can have many accounts but you used a first() in your function I used a hasOne here.
Using Eloquent relationships you can write in your User model:
<?php
public function account()
{
// I assume here 'username' is the local key for your User model
return $this->hasOne('Account', 'subdomain', 'username');
}
public function products()
{
// You should really have a user_id in your User Model
// so that you will not have to use information from the
// user's account
return $this->hasMany('Product', 'account_id', 'user_id');
}
You should define the belongsTo in your Account model and Product model.
With Eager Loading you will not run a lot of SQL queries : http://laravel.com/docs/eloquent#eager-loading
You will be able to use something like
$users = User::with('account', 'products')->get();
To get all users with their account and products.
I think this is a good example for the purpose of Repositories.
You shouldn't query the (involved) models directly but wrap them up into a ProductRepository (or Repositories in general) that handles all the queries.
For instance:
<?php
class ProductRepository
{
protected $accountId;
public function __construct($accountId)
{
$this->accountId = $accountId;
}
public function all()
{
return Product::where('account_id', $this->accountId)->get();
}
}
//now bind it to the app container to make it globaly available
App::bind('ProductRepository', function() {
return new ProductRepository(Auth::user()->account()->id);
});
// and whenever you need it:
$productRepository = App::make('ProductRepository');
$userProducts = $productRepository->all();
You could group the relevant routes and apply a filter on them in order to bind it on each request so the account-id would be queried only once per repository instance and not on every single query.
Scopes could also be interesting in this scenario:
// app/models/Product.php
public function scopeCurrentAccount($query)
{
return $query->where('account_id', Auth::user()->account()->id);
}
Now you could simply call
$products = Product::currentAccount()->get();
I am using Authorize.net payment gateway in my magento based shopping cart. It is Authorize Only, which means I first authorize the card, an after shipping product to customer I captures the amount.
Authorize paygate stores information like cc_exp_month, cc_exp_year inside "additional_information" field in table sales_flat_order_payment in serialize form.
Is there any method in magento to simply retrieve these values (cc_exp_month, cc_exp_year) from additional_information column?
I believe the next 2 lines will do the trick:
$ccExpMonth = $order->getPayment()->getAdditionalInformation('cc_exp_month');
$ccExpYear = $order->getPayment()->getAdditionalInformation('cc_exp_year');
Of course $order is instance of Mage_Sales_Model_Order.
For reference you also can check: how to get payment information on Magento?
Is there any method in magento to simply retrieve these values (cc_exp_month, cc_exp_year) from additional_information column?
No, there isn't. You need to drill into the additional_information array. Here's a method I added to retrieve the cc_type value. It can easily be adjusted to return all the data or just another piece:
public function getCcType(Mage_Sales_Model_Order_Payment $payment)
{
if(count($payment->getAdditionalInformation()))
{
foreach($payment->getAdditionalInformation() as $auth_cards)
{
foreach($auth_cards as $ac_id)
{
if(isset($ac_id['cc_type']))
{
return $ac_id['cc_type'];
}
}
}
}
return false;
}
Try
$order_id = 113
$order = Mage::getModel('sales/order')->load($order_id);
If info is stored cc_exp_month and cc_exp_year
$ccExpMonth = $order->getPayment()->getCcExpMonth();
$ccExpYear = $order->getPayment()->getCcExpYear();
If info is stored in Additional Info
$ccExpMonth = $order->getPayment()->getAdditionalInformation('cc_exp_month');
$ccExpYear = $order->getPayment()->getAdditionalInformation('cc_exp_year');
The newsletter subscription module in Magento has only one field (email) by default. After I add an extra field to the form (say country), how can I get the form data to show up in the Magento back-end and be sent as an email to a preset recipient? Thanks.
If you want to add some custom fields for Magento newsletter subscriber (for example subscriber_name), you should do the following:
Add new column for newsletter_subscriber table
Add text input to newsletter template
Create observer for newsletter_subscriber_save_before event
In the observer you can get your custom field's value from request and assign it to subscriber's object:
public function newsletterSubscriberSave(Varien_Event_Observer $observer)
{
$subscriber = $observer->getEvent()->getSubscriber();
$name = Mage::app()->getRequest()->getParam('subscriber_name');
$subscriber->setSubscriberName($name);
return $this;
}
UPDATE:
Here is the detailed article explaining how to add Country field
Also, I have created a free module, it is available on the GitHub
There are a few things that you need to take care of to make this work:
Add a new column for your data to the appropriate database table
Make sure that Magento saves your new field to the database
Present the data in the admin backend
Record the data when you get a new newsletter subscription
Here's how you can do all those things:
Ad. 1)
Using phpMyAdmin, MySQL command line, or whatever is your preferred DB manipulation method, add a new column "country" as, say, varchar(100) to the newsletter_subscriber table.
Ad. 2)
Magento will automatically give you access to the new field through the getCountry() and setCountry() methods on the Mage_Newsletter_Model_Subscriber object. The only thing it won't do is save your field back to the DB after it has been changed with code somewhere in the system. To get it saved you need to modify _prepareSave(Mage_Newsletter_Model_Subscriber $subscriber) function found in Mage_Newsletter_Model_Mysql4_Subscriber (app/code/core/Mage/Newsletter/Model/Mysql4/Subscriber.php). Be sure to make a local copy of the file first and not modify the core file. Here's what you need to add:
protected function _prepareSave(Mage_Newsletter_Model_Subscriber $subscriber)
{
$data = array();
$data['customer_id'] = $subscriber->getCustomerId();
$data['store_id'] = $subscriber->getStoreId()?$subscriber->getStoreId():0;
$data['subscriber_status'] = $subscriber->getStatus();
$data['subscriber_email'] = $subscriber->getEmail();
$data['subscriber_confirm_code'] = $subscriber->getCode();
//ADD A NEW FIELD START
//note that the string index for the $data array
//must match the name of the column created in step 1
$data['country'] = $subscriber->getCountry();
//ADD A NEW FIELD END
(...)
}
Ad. 3)
You will need to modify (a local copy of) the file app/code/core/Mage/Adminhtml/Block/Newsletter/Subscriber/Grid.php. The method you are looking for is called _prepareColumns(). In there you will see a series of calls to $this->addColumn(). You need to add a corresponding call for your "Country" field with the following code:
$this->addColumn('country', array(
'header' => Mage::helper('newsletter')->__('Country'),
//the index must match the name of the column created in step 1
'index' => 'country',
'default' => '----'
));
If you want the field to appear at the end of the grid (as the last column) add it as the last call, otherwise, squeeze it between the existing calls exactly where you want it to end up in the admin.
Ad. 4)
This is a part I did not have to do in my customization of the Magento newsletter, so it will be mostly theoretical. The subscription occurs in the controller located at app/code/core/Mage/Newsletter/controllers/SubscriberController.php. Here's the code of the newAction method with my proposed changes:
public function newAction()
{
if ($this->getRequest()->isPost() && $this->getRequest()->getPost('email')) {
$session = Mage::getSingleton('core/session');
$email = (string) $this->getRequest()->getPost('email');
try {
if (!Zend_Validate::is($email, 'EmailAddress')) {
Mage::throwException($this->__('Please enter a valid email address'));
}
$status = Mage::getModel('newsletter/subscriber')->subscribe($email);
if ($status == Mage_Newsletter_Model_Subscriber::STATUS_NOT_ACTIVE) {
$session->addSuccess($this->__('Confirmation request has been sent'));
}
else {
$session->addSuccess($this->__('Thank you for your subscription'));
}
//ADD COUNTRY INFO START
//at this point we may safly assume that subscription record was created
//let's retrieve this record and add the additional data to it
$subscriber = Mage::getModel('newsletter/subscriber')->loadByEmail($email);
//assuming that the input's id is "country"
$subscriber->setCountry((string) $this->getRequest()->getPost('country'));
//don't forget to save the subscriber!
$subscriber->save();
//ADD COUNTRY INFO END
}
catch (Mage_Core_Exception $e) {
$session->addException($e, $this->__('There was a problem with the subscription: %s', $e->getMessage()));
}
catch (Exception $e) {
$session->addException($e, $this->__('There was a problem with the subscription'));
}
}
$this->_redirectReferer();
}
Going through the above steps should take care of the most part of your problem. Let me know how that last part worked out, as I did not have a chance to test it.
Once you have your additional field in the Subscriber object you can do whatever you want with it. I did not really get what you mean by
be sent as an email to a preset recipient
If you can explain that I will try to help you out with this part too.
Edit - how to send a mail when someone subscribes
Just add the following code to the controller after the part which adds country to a subscriber object.
$mail = new Zend_Mail();
$mail->setBodyHtml("New subscriber: $email <br /><br />Country: ".$this->getRequest()->getPost('country'));
$mail->setFrom("youremail#email.com")
->addTo("admin#mysite.com")
->setSubject("Your Subject here");
$mail->send();
Adding to the accepted answer, you can also get away with this a little easier if you're adding a date, datetime, or timestamp-type column.
In my case, I wanted to add a "Subscribed at Date" to my grid. To do this, I wrote my upgrade script, column type being TIMESTAMP and the default value being CURRENT_TIMESTAMP. This way, when the row is added, the current date/time is recorded.
Then, all you have to do is add your block customizations. I'd suggest doing it by extending Magento's grid block rather than doing the local codepool override though. This way, you only need to override _prepareColumns();
Old thread but if someone has the same question, there is a free extension, that adds fields for gender, firstname and lastname and makes it available in the backend grid for export via xml/csv: http://www.magentocommerce.com/magento-connect/extended-newsletter-subscription-for-guests.html
Perhaps you can extend the code to fit your needs.
This is a warning for anyone who's installed the Ebizmarts_MailChimp extension.
It's a great extension. But it adds subscriber_firstname and subscriber_lastname to the newsletter_subscriber table.
If you intend to create these fields, you should either "require" the Ebizmarts_MailChimp extension or check the fields don't exist before your extension creates them.
In the opposite, where you've created them and want to install the the Ebizmarts_MailChimp extension after you've created these fields, you will have to comment out the addColumn code for these two fields during installation.