I am using Magento 2.3.4 inside a docker container for a payment gateway extension. First things first, here is the affected code:
<?php
namespace Magento\PGM\Block;
use Magento\AdminNotification\Model\Inbox;
use Magento\Checkout\Model\Session;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\Response\Http;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
use Magento\Sales\Api\Data\OrderAddressInterface;
use Magento\Sales\Model\Order\Payment\Transaction;
use Magento\Sales\Model\Order\Payment\Transaction\Builder as TransactionBuilder;
use Magento\Sales\Model\OrderFactory;
use Magento\Store\Model\ScopeInterface;
use Magento\PGM\Logger\Logger;
class Main extends Template
{
protected $_objectmanager;
protected $checkoutSession;
protected $urlBuilder;
protected $response;
protected $config;
protected $messageManager;
protected $transactionBuilder;
protected $inbox;
private $logger;
private $orderFactory;
public function __construct(Context $context, Session $checkoutSession, OrderFactory $orderFactory = null, Logger $logger, Http $response, TransactionBuilder $tb, Inbox $inbox)
{
$this->checkoutSession = $checkoutSession;
$this->orderFactory = $orderFactory ?: ObjectManager::getInstance()->get(OrderFactory::class);
$this->response = $response;
$this->config = $context->getScopeConfig();
$this->transactionBuilder = $tb;
$this->logger = $logger;
$this->inbox = $inbox;
$this->urlBuilder = ObjectManager::getInstance()
->get('Magento\Framework\UrlInterface');
parent::__construct($context);
}
public function getParentId()
{
return $this->getData(OrderAddressInterface::PARENT_ID);
}
protected function _prepareLayout()
{
$method_data = array();
$order = $this->orderFactory->create()->load($this->getParentId());
if ($order) {
$payment = $order->getPayment();
// The error is thrown here (" Call to a member function setTransactionId() on null")
$payment->setTransactionId("-1");
...
$payment->save();
$order->save();
...
}
private function setApiData($order, $testmode, $instance)
{
...
}
}
I am getting this error:
Call to a member function setTransactionId() on null
I think that this is just a symptom though. The order object is not created, my IDE marks the $order->getPayment() method as not found at all.
The code itself should not be the problem, but the folder 'Sales\Model' does not contain an orderFactory.php file. Is the file missing or deprecated? Several modules use this file and create orders like this, for example the Paypal PGM, and use the OrderFactory.php file.
As i know The Factory class name is the name of Model class and append with the Factory word. So for our example, we will have TopicFactory class. You must not create this class. Magento will create it for you. Whenever Magento’s object manager encounters a class name that ends in the word ‘Factory’, it will automatically generate the Factory class in the var/generation folder if the class does not already exist. You will see the factory class in
ROOT/generated/code/<vendor_name>/<module_name>/Model/OrderFactory.php
So the first step you should go to the folder Generation to see the class is there or NOT.
If it's not there, i think you're are facing permission issue , magento cant generate (can't create file or folder) the Factory Class in Generation folder.
Hi orderFactory does not have payment in DB, so you cannot use this to get payment. You can try this:
use Magento\Sales\Model\ResourceModel\Order\Payment\Transaction\CollectionFactory;
protected $transactions;
public function __constructor(CollectionFactory $transactions)
{
$this->transactions = $transactions;
}
In your method:
$transactions = $this->transactions->create()->addOrderIdFilter($orderId);
...
$transactions->setTransactionId("-1");`
Related
Laravel 8 has the default App/Models directory for Model classes. The Illuminate\Database\Eloquent\Factories\Factory has static function resolveFactoryName() to resolve name of ModelNameFactory class
public static function resolveFactoryName(string $modelName)
{
$resolver = static::$factoryNameResolver ?: function (string $modelName) {
$modelName = Str::startsWith($modelName, 'App\\Models\\')
? Str::after($modelName, 'App\\Models\\')
: Str::after($modelName, 'App\\');
return static::$namespace.$modelName.'Factory';
};
return $resolver($modelName);
}
The function works properly only for App/ModelName or App/Models/ModelName
if name of Model class, for example, is the Domain/Customers/Models/ModelName, that function doesn't work properly. What is the best way to fix it?
As you can see here, there is a method called guessFactoryNamesUsing which lets you tell Laravel how it should guess the name of your factories.
Add the following to your AppServiceProvider:
use Illuminate\Database\Eloquent\Factories\Factory;
public function register()
{
Factory::guessFactoryNamesUsing(function ($class) {
return 'Database\\Factories\\' . class_basename($class) . 'Factory';
});
}
Source:
/**
* Specify the callback that should be invoked
* to guess factory names based on dynamic relationship names.
*
* #param callable $callback
* #return void
*/
public static function guessFactoryNamesUsing(callable $callback)
{
static::$factoryNameResolver = $callback;
}
Please put this in your model class in App\Models\ModelName.
Make sure the ModelFactory is the factory name.
protected static function newFactory()
{
return \Modules\Module\Database\Factories\ModelFactory::new();
}
i am new in magento2. i have retrieve records from table and showed in grid but now i am trying to Insert/data in a custom table from a form but not getting any help. Can you pls guide me how can i Insert data in custom table using block class and resource model etc. i mean standard way to insert data.
Here is block class
class Insert extends Template
{
private $collectionFactory;
public $successMessage=null;
protected $data;
public function __construct(
Template\Context $context,
CollectionFactory $collectionFactory,
array $data = []
) {
$this->collectionFactory = $collectionFactory;
parent::__construct($context, $data);
}
public function execute()
{
$name = $this->getRequest()->getParam('bookName');
$author = $this->getRequest()->getParam('bookAuthor');
$description = $this->getRequest()->getParam('bookDescription');
$model = $this->collectionFactory->create();
$model->load($name);
$model->load($author);
$model->load($description);*/
$input = $this->getRequest()->getPostValue();
$model->setData($input);
$model->save();
$successMessage="Saved Successfully!";
}
}
-----------These are model classes---------------
class Book extends AbstractModel
{
protected function _construct()
{
$this->_init(\Vendor\BooksModule\Model\ResourceModel\Book::class);
}
}
class Book extends AbstractDb
{
protected function _construct()
{
$this->_init('Vendor_BooksModule_Book', 'id');
}
}
----------------This is resource model-----------------
class Collection extends AbstractCollection
{
protected $_idFieldName = 'id';
protected function _construct()
{
$this->_init("Vendor\BooksModule\Model\Book","Vendor\BooksModule\Model\ResourceModel\Book");
}
}
Thank you
First, need to create a customer module. some instruction here https://www.mageplaza.com/magento-2-module-development/how-create-hello-world-module-magento-2.html
After creating a custom module you need a create your custom table. and resource model using that link. https://www.mageplaza.com/magento-2-module-development/how-to-create-crud-model-magento-2.html
After creating a table and model you can save data inside a block. Inside block can data using getParam. But want to change like that.
public function execute()
{
$name = $this->getRequest()->getParam('bookName');
$author = $this->getRequest()->getParam('bookAuthor');
$description = $this->getRequest()->getParam('bookDescription');
$model = $this->collectionFactory->create();
$model = $model->getCollection()->addFieldToFilter('bookName', $name)->addFieldToFilter('bookAuthor', $author)->addFieldToFilter('bookDescription', $description);
$input = $this->getRequest()->getPostValue();
$model->setData($input);
$model->save();
$successMessage="Saved Successfully!";
}
We can collection only load with id. that is wrong you done. When use setData you also want to add attribute name. check and change that.
I'm trying to set an uuid as primary key in a Laravel Model. I've done it setting a boot method in my model as stablished here so I don't have to manually create it everytime I want to create and save the model. I have a controller that just creates the model and saves it in database.
It is saved correctly in database but when controller returns the value of the id is always returned with 0. How can I make it to actually return the value that it is creating in database?
Model
class UserPersona extends Model
{
protected $guarded = [];
protected $casts = [
'id' => 'string'
];
/**
* Setup model event hooks
*/
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$uuid = Uuid::uuid4();
$model->id = $uuid->toString();
});
}
}
Controller
class UserPersonaController extends Controller
{
public function new(Request $request)
{
return UserPersona::create();
}
}
You need to change the keyType to string and incrementing to false. Since it's not incrementing.
public $incrementing = false;
protected $keyType = 'string';
Additionally I have an trait which I simply add to those models which have UUID keys. Which is pretty flexible. This comes originally from https://garrettstjohn.com/articles/using-uuid-laravel-eloquent-orm/ and I added some small adjustments to it for issues which I have discovered while using it intensively.
use Illuminate\Database\Eloquent\Model;
use Ramsey\Uuid\Uuid;
/**
* Class Uuid.
* Manages the usage of creating UUID values for primary keys. Drop into your models as
* per normal to use this functionality. Works right out of the box.
* Taken from: http://garrettstjohn.com/entry/using-uuids-laravel-eloquent-orm/
*/
trait UuidForKey
{
/**
* The "booting" method of the model.
*/
public static function bootUuidForKey()
{
static::retrieved(function (Model $model) {
$model->incrementing = false; // this is used after instance is loaded from DB
});
static::creating(function (Model $model) {
$model->incrementing = false; // this is used for new instances
if (empty($model->{$model->getKeyName()})) { // if it's not empty, then we want to use a specific id
$model->{$model->getKeyName()} = (string)Uuid::uuid4();
}
});
}
public function initializeUuidForKey()
{
$this->keyType = 'string';
}
}
Hope this helps.
Accepted answer not worked for me on Laravel 9, but this way worked perfect, you can try it:
1- Create new Trait Class in project path app/Traits/IdAsUuidTrait.php (if you not found Traits folder create it, this is full code of this Class:
<?php
namespace App\Traits;
use Illuminate\Support\Str;
trait IdAsUuidTrait
{
public function initializeIdAsUuidTrait(): void
{
$this->keyType = 'string';
$this->id = Str::orderedUuid()->toString();
}
}
2- In any model you want to make id as UUID just call trait like this:
use App\Traits\IdAsUuidTrait;
class YourModelName extends Model
{
use IdAsUuidTrait;
...
That is it, now try to create, select, update any row in database by this model...
I understand that the default Eloquent\Collection class can be overridden in your model by using the method:
public function newCollection(array $models = array()) {
return new CustomCollection($models);
}
Which works great if I'm using typical queries such as:
Model::where('name', $name)->get();
This is great so I can add methods to the eloquent collection class, such as:
$records = Model::where('name', $name)->get();
$records->toTable();
But if I'm using pagination on the model, for example:
Model::where('name', $name)->paginate(25);
It returns an instance of the class Illuminate\Support\Collection instead of the Illuminate\Database\Eloquent\Collection.
Is there a way of overriding or extending the typical Illuminate\Support\Collection?
I'm trying to add a toTable() method to the returned Collection. I'd rather not have to replace the pagination service provider with my own.
Thanks!!
You will need to replace the pagination service provider, amongst a couple of other classes in the pagination library. By the sound of it you know how to do it this way, but were hoping for another answer, but as I have the code I'll drop it in here for you.
The reason you need to replace these classes/methods is because the files in Illuminate directly reference instances of classes within the Illuminate namespace.
In config/app.php
Replace
'Illuminate\Pagination\PaginationServiceProvider',
With
'ExtendedPaginationServiceProvider',
Create a new file somewhere the autoloader is capable of finding it called ExtendedPaginationServiceProvider.php and place the following in it
<?php
use Illuminate\Support\ServiceProvider;
class ExtendedPaginationServiceProvider extends ServiceProvider
{
/**
* #inheritdoc
*/
public function register()
{
$this->app->bindShared('paginator', function($app)
{
$paginator = new ExtendedPaginationFactory($app['request'], $app['view'], $app['translator']);
$paginator->setViewName($app['config']['view.pagination']);
$app->refresh('request', $paginator, 'setRequest');
return $paginator;
});
}
}
Create a new file somewhere the autoloader is capable of finding it called ExtendedPaginationFactory.php and place the following in it
<?php
use Illuminate\Pagination\Factory;
class ExtendedPaginationFactory extends Factory
{
/**
* #inheritdoc
*/
public function make(array $items, $total, $perPage = null)
{
$paginator = new ExtendedPaginationPaginator($this, $items, $total, $perPage);
return $paginator->setupPaginationContext();
}
}
Create a new file somewhere the autoloader is capable of finding it called ExtendedPaginationPaginator.php and place the following in it
<?php
use Illuminate\Pagination\Paginator;
class ExtendedPaginationPaginator extends Paginator
{
/**
* Get a collection instance containing the items.
*
* #return ExtendedCollection
*/
public function getCollection()
{
return new ExtendedCollection($this->items);
}
}
You'll notice the above returns a new instance of ExtendedCollection. Obviously replace this with your CustomCollection class you refer to in your question.
For others to reference, an ExtendedCollection class may look similar to the below
Create a new file somewhere the autoloader is capable of finding it called ExtendedCollection.php and place the following in it
<?php
use Illuminate\Support\Collection;
class ExtendedCollection extends Collection
{
}
Also, after creating these files, don't forget to run the following in the terminal
composer dump-autoload
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.