I'm creating a content Element with gridelments where i need to render some Data from the childs directly. There is no problem for fields like "bodytext" or "header". But the assets gives me only a counter, not a reference or path.
So the big question: How can i render the assets images from the childs?
I can share a VH I just did
<?php
namespace GeorgRinger\Theme\ViewHelpers;
use TYPO3\CMS\Core\Database\DatabaseConnection;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3\CMS\Frontend\Resource\FileCollector;
class FalViewHelper extends AbstractViewHelper
{
/**
* #var boolean
*/
protected $escapeOutput = FALSE;
/**
* #param string $table
* #param string $field
* #param string $id
* #param string $as
* #return string
*/
public function render($table, $field, $id, $as = 'references')
{
$row = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', $table, 'uid=' . (int)$id);
if (!$row) {
return '';
}
$fileCollector = GeneralUtility::makeInstance(FileCollector::class);
$fileCollector->addFilesFromRelation($table, $field, $row);
$this->templateVariableContainer->add($as, $fileCollector->getFiles());
$output = $this->renderChildren();
$this->templateVariableContainer->remove($as);
return $output;
}
/**
* #return DatabaseConnection
*/
protected function getDatabaseConnection()
{
return $GLOBALS['TYPO3_DB'];
}
}
Of course you need to adopt the namspace.
Usage would be
<theme:fal table="tt_content" field="assets" id=" => the id <= ">
<f:debug>{references}</f:debug>
</theme:fal>
Related
I want to attach invoice pdf instead of packing slip while creating shipment.
I am using Fooman Email attachment extension version 2.0.8
My magento is 2.2.5 Can anyone know how can I change the attched PDF in shipping confirmation mail ?
Currently it is attaching packing slip but I want to attach invoice pdf in shipping confirmation mail.
We had a similar problem using fooman. We also wanted to send our invoice on shipment creation, while disableing the standard transactional invoice mail on invoice creation. I wrote a module that sends the invoice email together with the shipping email, which is not exactly what you are looking for but maybe you can leverage that.
The module is pretty simple. Except from boilerplate registration.php and module.xml, all you need is to override InvoiceOrder from Magento\Sales\Model and comment out this line:
$this->notifierInterface->notify($order, $invoice, $comment);
like so:
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Sales\Model;
use Magento\Framework\App\ResourceConnection;
use Magento\Sales\Api\Data\InvoiceCommentCreationInterface;
use Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface;
use Magento\Sales\Api\InvoiceOrderInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\Config as OrderConfig;
use Magento\Sales\Model\Order\Invoice\NotifierInterface;
use Magento\Sales\Model\Order\InvoiceDocumentFactory;
use Magento\Sales\Model\Order\InvoiceRepository;
use Magento\Sales\Model\Order\OrderStateResolverInterface;
use Magento\Sales\Model\Order\PaymentAdapterInterface;
use Magento\Sales\Model\Order\Validation\InvoiceOrderInterface as InvoiceOrderValidator;
use Psr\Log\LoggerInterface;
/**
* Class InvoiceOrder
* #SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class InvoiceOrder implements InvoiceOrderInterface
{
/**
* #var ResourceConnection
*/
private $resourceConnection;
/**
* #var OrderRepositoryInterface
*/
private $orderRepository;
/**
* #var InvoiceDocumentFactory
*/
private $invoiceDocumentFactory;
/**
* #var PaymentAdapterInterface
*/
private $paymentAdapter;
/**
* #var OrderStateResolverInterface
*/
private $orderStateResolver;
/**
* #var OrderConfig
*/
private $config;
/**
* #var InvoiceRepository
*/
private $invoiceRepository;
/**
* #var InvoiceOrderValidator
*/
private $invoiceOrderValidator;
/**
* #var NotifierInterface
*/
private $notifierInterface;
/**
* #var LoggerInterface
*/
private $logger;
/**
* InvoiceOrder constructor.
* #param ResourceConnection $resourceConnection
* #param OrderRepositoryInterface $orderRepository
* #param InvoiceDocumentFactory $invoiceDocumentFactory
* #param PaymentAdapterInterface $paymentAdapter
* #param OrderStateResolverInterface $orderStateResolver
* #param OrderConfig $config
* #param InvoiceRepository $invoiceRepository
* #param InvoiceOrderValidator $invoiceOrderValidator
* #param NotifierInterface $notifierInterface
* #param LoggerInterface $logger
* #SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
ResourceConnection $resourceConnection,
OrderRepositoryInterface $orderRepository,
InvoiceDocumentFactory $invoiceDocumentFactory,
PaymentAdapterInterface $paymentAdapter,
OrderStateResolverInterface $orderStateResolver,
OrderConfig $config,
InvoiceRepository $invoiceRepository,
InvoiceOrderValidator $invoiceOrderValidator,
NotifierInterface $notifierInterface,
LoggerInterface $logger
) {
$this->resourceConnection = $resourceConnection;
$this->orderRepository = $orderRepository;
$this->invoiceDocumentFactory = $invoiceDocumentFactory;
$this->paymentAdapter = $paymentAdapter;
$this->orderStateResolver = $orderStateResolver;
$this->config = $config;
$this->invoiceRepository = $invoiceRepository;
$this->invoiceOrderValidator = $invoiceOrderValidator;
$this->notifierInterface = $notifierInterface;
$this->logger = $logger;
}
/**
* #param int $orderId
* #param bool $capture
* #param array $items
* #param bool $notify
* #param bool $appendComment
* #param \Magento\Sales\Api\Data\InvoiceCommentCreationInterface|null $comment
* #param \Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface|null $arguments
* #return int
* #throws \Magento\Sales\Api\Exception\DocumentValidationExceptionInterface
* #throws \Magento\Sales\Api\Exception\CouldNotInvoiceExceptionInterface
* #throws \Magento\Framework\Exception\InputException
* #throws \Magento\Framework\Exception\NoSuchEntityException
* #throws \DomainException
*/
public function execute(
$orderId,
$capture = false,
array $items = [],
$notify = false,
$appendComment = false,
InvoiceCommentCreationInterface $comment = null,
InvoiceCreationArgumentsInterface $arguments = null
) {
$connection = $this->resourceConnection->getConnection('sales');
$order = $this->orderRepository->get($orderId);
$invoice = $this->invoiceDocumentFactory->create(
$order,
$items,
$comment,
($appendComment && $notify),
$arguments
);
$errorMessages = $this->invoiceOrderValidator->validate(
$order,
$invoice,
$capture,
$items,
$notify,
$appendComment,
$comment,
$arguments
);
if ($errorMessages->hasMessages()) {
throw new \Magento\Sales\Exception\DocumentValidationException(
__("Invoice Document Validation Error(s):\n" . implode("\n", $errorMessages->getMessages()))
);
}
$connection->beginTransaction();
try {
$order = $this->paymentAdapter->pay($order, $invoice, $capture);
$order->setState(
$this->orderStateResolver->getStateForOrder($order, [OrderStateResolverInterface::IN_PROGRESS])
);
$order->setStatus($this->config->getStateDefaultStatus($order->getState()));
$invoice->setState(\Magento\Sales\Model\Order\Invoice::STATE_PAID);
$this->invoiceRepository->save($invoice);
$this->orderRepository->save($order);
$connection->commit();
} catch (\Exception $e) {
$this->logger->critical($e);
$connection->rollBack();
throw new \Magento\Sales\Exception\CouldNotInvoiceException(
__('Could not save an invoice, see error log for details')
);
}
if ($notify) {
if (!$appendComment) {
$comment = null;
}
//$this->notifierInterface->notify($order, $invoice, $comment);
}
return $invoice->getEntityId();
}
}
Now all you have to do is setup events.xml and observe sales_order_shipment_save_after
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name='sales_order_shipment_save_after'>
<observer name='SendInvoiceWithShipment' instance='Vendor\Module\Observer\SendInvoiceWithShipment'
/>
</event>
</config>
Observer leverages standard Magento 2 transactional email $this->_invoiceSender->send($invoice); like so:
<?php
namespace Vendor\Module\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Email\Sender\InvoiceSender;
class SendInvoiceWithShipment implements ObserverInterface
{
protected $_invoiceSender;
public function __construct(
InvoiceSender $invoiceSender
) {
$this->_invoiceSender = $invoiceSender;
}
public function execute(\Magento\Framework\Event\Observer $observer)
{
$order = $observer->getShipment()->getOrder();
if (!$order) {
// Dont send invoice if order is not provided
return;
}
$invoices = $order->getInvoiceCollection();
foreach ($invoices as $invoice) {
try {
$this->_invoiceSender->send($invoice);
} catch (\Exception $e) {
// Do something if failed to send
}
}
}
}
I created a custom widget viewhelper for an extension which works fine in an empty typo3 8.7 installation. But when I use it on needed project with same code it causes an error:
#1289422564: initiateSubRequest() can not be called if there is no valid controller extending TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController Got "NULL" in class ...
Did somebody have an error like this before or does someone know what causes this type of error?
<!-- This is the View List.html-->
{namespace Exhibitors = MyVendorName\MyExhibitors\ViewHelpers}
<ul class="second_lvl">
<Exhibitors:widget.AtoZNav objects="{feUserData}" as="filteredExhibitors" property="company">
<Exhibitors:widget.sort objects="{filteredExhibitors}" as="sortedExhibitors" property="company">
<f:for each="{filteredExhibitors}" as="feUser">
<li class="navLink">
{feUser.company}<br />
{feUser.company}
{feUser.www}<br />
</li>
</f:for>
</Exhibitors:widget.sort>
</Exhibitors:widget.AtoZNav>
</ul>
<f:link.action action="show">show detail page</f:link.action>
<?php
/**
* This is the SortViewHelper
*/
namespace MyVendorName\MyExhibitors\ViewHelpers\Widget;
class SortViewHelper extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper
{
/**
* #param \TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects
* #param string $as
* #param string $property
* #return string
*/
public function render(\TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects, $as, $property)
{
return $this->initiateSubRequest();
}
}
<?php
/**
* This is the Sort Controller
*/
namespace MyVendorName\MyExhibitors\ViewHelpers\Widget\Controller;
class SortController extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController
{
/**
* #var \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
protected $objects;
public function initializeAction()
{
$this->objects = $this->widgetConfiguration['objects'];
}
/**
* #param mixed string $order
*/
public function indexAction($order = \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING)
{
$order = ($order == \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING) ? \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING : \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING;
$query = $this->objects->getQuery();
$query->setOrderings(array($this->widgetConfiguration['property'] => $order));
$modifiedObjects = $query->execute();
$this->view->assign('contentArguments', array($this->widgetConfiguration['as'] => $modifiedObjects));
$this->view->assign('order', $order);
}
}
<?php
/**
* This is AtoZNav ViewHelper
*/
namespace MyVendorName\MyExhibitors\ViewHelpers\Widget;
class AtoZNavViewHelper extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper
{
/**
* #param \TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects
* #param string $as
* #param string $property
* #return string
*/
public function render(\TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects, $as, $property)
{
return $this->initiateSubRequest();
}
}
<?php
/**
* This is the Controller
*/
namespace MyVendorName\MyExhibitors\ViewHelpers\Widget\Controller;
class AtoZNavController extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetController
{
/**
* #var \TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
protected $objects;
public function initializeAction()
{
$this->objects = $this->widgetConfiguration['objects'];
}
/**
* #param string $char
* #throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException
*/
public function indexAction($char = '%')
{
$query = $this->objects->getQuery();
$query->matching($query->like($this->widgetConfiguration['property'], $char . '%'));
$modifiedObjects = $query->execute();
$this->view->assign('contentArguments', array($this->widgetConfiguration['as'] => $modifiedObjects));
$this->view->assign('letters', range('A', 'Z'));
$this->view->assign('char', $char);
}
}
I got the same error. You need to inject your controller, like it is done in the fluid pagination widget. This part is missing in the book.
<?php
/**
* This is the SortViewHelper
*/
namespace MyVendorName\MyExhibitors\ViewHelpers\Widget;
class SortViewHelper extends \TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper
{
protected $controller;
/**
* #param Controller/SortController $controller
*/
public function injectSortController(Controller\SortController $controller)
{
$this->controller = $controller;
}
/**
* #param \TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects
* #param string $as
* #param string $property
* #return string
*/
public function render(\TYPO3\CMS\Extbase\Persistence\QueryResultInterface $objects, $as, $property)
{
return $this->initiateSubRequest();
}
}
I am trying to get translated FAL image in extbase but it gives me default language FAL image.
I am using TYPO3 7.6.16. Its a multi-language website.
I have created 2 website languages 1) English, 2) Spanish and the default one is Dutch.
Currently I am fetching data from repository and it gives me model with the same FAL image in both translated version and in original version of the record.
How Can I get translated FAL image using extbase (not in Fluid), because I want to return it to JSON response?
Here is the code:
Controller:
$posts = $this->postRepository->findByLanguage($langId);
foreach($posts as $post) {
$output[] = [
'uid' => $post->getUid(),
'title' => $post->getTitle(),
'image' => $post->getImage()->getOriginalResource()->getOriginalFile()->getPublicUrl()
];
}
header('Content-Type: application/json');
echo json_encode($output);
exit();
Here I am getting default language FAL image instead of localised on line $post->getImage()->getOriginalResource()->getOriginalFile()->getPublicUrl()
Repository:
/**
* #return array|\TYPO3\CMS\Extbase\Persistence\QueryResultInterface
*/
public function findByLanguage($langId, $postId = 0)
{
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(FALSE);
$query->getQuerySettings()->setRespectSysLanguage(TRUE);
$query->getQuerySettings()->setLanguageUid($langId);
if ($postId) {
$query->matching(
$query->equals('uid' , $postId)
);
return $query->execute()->getFirst();
}
return $query->execute();
}
Model:
/**
* title
*
* #var string
*/
protected $title;
/**
* image
*
* #var \TYPO3\CMS\Extbase\Domain\Model\FileReference
*/
protected $image = null;
/**
* #return bool $title
*/
public function getTitle() {
return $this->title;
}
/**
* #param string $title
* #return void
*/
public function setTitle($title) {
$this->title = $title;
}
/**
* #return \TYPO3\CMS\Extbase\Domain\Model\FileReference $image
*/
public function getImage() {
return $this->image;
}
/**
* #param \TYPO3\CMS\Extbase\Domain\Model\FileReference $image
* #return void
*/
public function setImage(\TYPO3\CMS\Extbase\Domain\Model\FileReference $image) {
$this->image = $image;
}
There was a bug where the FAL records (sys_file_reference) of translated (copy translate) contents did not get the lanuguage id of the translation.
I made a bugfix for that: https://github.com/BenjaminBeck/bdm_bugfix_translatecopy - maybe thats your issue?
In my extbase extension I have model "Item".
I get images like
/**
* image
*
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference>
* #cascade remove
*/
protected $image = NULL;
In table "sys_file_reference" and in TCA I'm added filed "preview". It's work in backend.
In fluid I can get image uid from sys_file_reference like {image.uid}
How I can get "preview" in fluid from table sys_file_reference? This field isset in table.
You need to provide your own FileReference class that has your preview property and extends the Extbase model:
namespace Vendor\Extension\Domain\Model;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
class MyFileReference extends FileReference
{
/**
* #var string
*/
protected $preview;
// Getter and Setter
}
Then use this class in your $image property:
/**
* image
*
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Extension\Domain\Model\MyFileReference>
* #cascade remove
*/
protected $image = NULL;
Tell extbase in TypoScript to map your class to the sys_filke_reference table:
config.tx_extbase.persistence.classes {
Vendor\Extension\Domain\Model\MyFileReference {
mapping {
tableName = sys_file_reference
}
}
}
Code is untested but should point you in the right direction.
$fileRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\FileRepository::class);
$fileObjects = $fileRepository->findByRelation(
'tx_faktesemilivevideo_domain_model_subcategory',
'icon_image',
$uid
);
if (!empty($fileObjects)) {
foreach ($fileObjects as $key => $value) {
$imagePath = $baseUrl .
'fileadmin' .
$value->getOriginalFile()->getIdentifier();
}
} else {
//default image path.
}
Simple one hopefully, is there a specific way i should be updating a single database value using a model in Zend Framework.
I currently do this:
class Model_MyModel extends Zend_Db_Table_Abstract
{
$_name = 'table';
public function updateSetting($id,$status)
{
$data = array(
'status' => $status
);
$this->update($data, 'id = '.$id);
}
}
$update = new Model_MyModel();
$update->updateSetting(10,1);
Obviously i could pass in another argument as the column to update. I just wondered if there was a more "magic" way i should be doing this?
You could write a simple property overloader for this:
class Model_MyModel extends Zend_Db_Table_Abstract
{
protected $_name = 'table';
/**
* Should be a Zend_Db_Table_Row instance
*
* #var Zend_Db_Table_Row
*/
protected $_currentRow = null;
/**
* Property overloader
*
* For more information on this see
* http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members
*
* #param string $key
* #param string $value
* #return void
*/
public function __set($key, $value)
{
$row = $this->getCurrentRow();
if (null !== $row)
{
$row->$key = $value;
}
else
{
throw new Exception('Cannot update a column on a non existent row!');
}
}
/**
* Save current row
*
* #return Model_MyModel
*/
public function saveCurrentRow()
{
$row = $this->getCurrentRow();
if (null !== $row)
{
$row->save();
}
else
{
throw new Exception('Cannot save a non existent row!');
}
}
/**
* Set current row
*
* #param Zend_Db_Table_Row $row
* #return Model_MyModel
*/
public function setCurrentRow(Zend_Db_Table_Row $row)
{
$this->_currentRow = $row;
return $this;
}
/**
* Get current row
*
* #return Zend_Db_Table_Row
*/
public function getCurrentRow()
{
return $this->_currentRow;
}
}
You could then do stuff like this:
$model = new Model_MyModel();
$model->status = 'foo';
$model->somecolumn = 'bar'
$model->saveCurrentRow();
Although this approach would require the least editing in your code, an even better approach would be seperating your models from your database tables and use a Data Mapper Pattern as described in the Quickstart.