Show xml data in Frontend - typo3

Is it possible fetching data from a cached xml file and then showing them on front end?
I was thinking doing it in a TYPO3 extension and with its domain model (and getter/setter) but without a database table. And then filling in data with SimpleXML just to "store" them in memory. At least display the data from domain model with fluid on front end. But I don't know is this approach right or is there a better way to do that? In particular setting up the persistence layer I don't understand.
For any help I thank you very much for your effort in advance.

I found an "acceptable" solution. My approach for that was:
Get all items from xml file
Add a slug field
Sort the items
Display sorted items on the front end
Create unique pretty url
1. Get all items from xml file
Controller: listAction, detailAction
public function listAction() {
$jobs = $this->xmlDataRepository->findAll();
$jobsArray = $this->simpleXmlObjToArr($jobs);
$jobsArraySorted = $this->sortJobsByTitle($jobsArray);
$this->view->assign('jobs', $jobsArraySorted);
}
public function detailAction($slugid) {
$job = $this->xmlDataRepository->findBySlugWithId($slugid);
$this->view->assign('job', $job[0]);
}
Repository: findAll, findBySlugWithId
public function findAll() {
$objectStorage = new ObjectStorage();
$dataFolder = ConfigurationService::setDataFolder();
$xmlFile = glob($dataFolder . '*.xml')[0];
$xmlData = simplexml_load_file($xmlFile,'SimpleXMLElement',LIBXML_NOWARNING);
// error handling
if ($xmlData === false) {
...
}
foreach($xmlData->children() as $job) {
$objectStorage->attach($job);
}
return $objectStorage;
}
public function findBySlugWithId($slugid) {
// get id from slugid
$id = substr($slugid,strrpos($slugid,'-',-1)+1);
$objectStorage = new ObjectStorage();
$dataFolder = ConfigurationService::setDataFolder();
$xmlFile = glob($dataFolder . '*.xml')[0];
$xmlData = simplexml_load_file($xmlFile,'SimpleXMLElement',LIBXML_NOWARNING);
// error handling
if ($xmlData === false) {
...
}
$jobfound = false;
foreach($xmlData->children() as $job) {
if ($job->JobId == $id) {
$objectStorage->attach($job);
$jobfound = true;
}
}
// throw 404-error
if (!$jobfound) {
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
$GLOBALS['TYPO3_REQUEST'],
'Ihre angeforderte Seite wurde nicht gefunden',
['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
);
throw new ImmediateResponseException($response, 9000006460);
}
return $objectStorage;
}
2. Add a slug field (controller)
protected function simpleXmlObjToArr($obj) {
// 2-dimensional array
$array = [];
foreach($obj as $item){
$row = [];
foreach($item as $key => $val){
$row[(string)$key] = (string)$val;
}
//add slug field, build it with Title
$row['Slug'] = $this->convertToPathSegment($row['Titel']);
// add $row to $array
array_push($array,$row);
}
return $array;
}
3. Sort the items (controller)
protected function sortJobsByTitle(array $jobs) {
$title = array();
$id = array();
foreach ($jobs as $key => $job) {
$title[$key] = $job['Titel'];
$id[$key] = $job['JobId'];
}
// sort jobs array according to title, uid (uid because if there are courses with the same title!)
array_multisort($title,SORT_ASC, $id,SORT_ASC, $jobs,SORT_STRING);
return $jobs;
}
4. Display sorted items on the front end (templates)
List.html:
...
<ul>
<f:for each="{jobs}" as="job">
<li>
<f:comment>
<f:link.action class="" pageUid="2" action="show" arguments="{id: job.JobId, slug: job.Slug}">{job.Titel}</f:link.action> ({job.JobId})<br>
<f:link.action class="" pageUid="2" action="detail" arguments="{xml: job}">NEW {job.Titel}</f:link.action> ({job.JobId})
</f:comment>
<f:variable name="slugid" value="{job.Slug}-{job.JobId}"/>
<f:link.action class="" pageUid="2" action="detail" arguments="{slugid: slugid}"><f:format.raw>{job.Titel}</f:format.raw></f:link.action> ({job.JobId})
</li>
</f:for>
</ul>
...
Detail.html:
...
<f:image src="{job.Grafik}" width="500" alt="Detailstellenbild" />
<p><strong><f:format.raw>{job.Titel}</f:format.raw></strong> ({job.JobId})</p>
<p>Region: {job.Region}</p>
<f:format.html>{job.Beschreibung}</f:format.html>
...
5. Create unique pretty url
...
routeEnhancers:
XmlJobDetail:
type: Extbase
limitToPages:
- 2
extension: Wtdisplayxmldata
plugin: Displayxmldata
routes:
-
routePath: '/{job-slugid}'
_controller: 'XmlData::detail'
_arguments:
job-slugid: slugid
defaultController: 'XmlData::list'
aspects:
job-slugid:
type: XmlDetailMapper
Routing/Aspect/XmlDetailMapper.php:
use TYPO3\CMS\Core\Routing\Aspect\StaticMappableAspectInterface;
use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
class XmlDetailMapper implements StaticMappableAspectInterface {
/**
* {#inheritdoc}
*/
public function generate(string $value): ?string
{
return $value !== false ? (string)$value : null;
}
/**
* {#inheritdoc}
*/
public function resolve(string $value): ?string
{
return isset($value) ? (string)$value : null;
}
}

Related

How to change the order of the product list in cart_products in TYPO3 11?

I would like to create links that let the user sort the product list in cart_products ascending and descending.
For this I created a Fluid-link in Grid.html of cart_products that passes an argument for sorting to the controller:
<f:link.action action="list" arguments="{sorting:'up'}">Sorting up</f:link.action>
The method listAction() in ProductController.php gets the argument with:
if ($this->request->hasArgument('sorting')) {
$sorting = $this->request->getArgument('sorting');
}
With this if-statement I control what is happening based on the given argument:
if ($sorting === "up") {
// Get all products sorted ascending
} elseif ($sorting === "down"){
// Get all products sorted decending
}
The products are received with the following command (original):
$products = $this->productRepository->findDemanded($demand);
The documentation says that the following function does the sorting:
$query->setOrderings(
[
'organization.name' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
'title' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING
]
);
I would like to know how to combine the both parts to receive the products ordered as wished.
Change your view helper to directly use sorting argument
<f:link.action action="listSorted" arguments="{sorting:'asc'}">Sorting up</f:link.action>
Add your own controller action (extending class ProductController)
<?php
// ...
public function listSortedAction(int $currentPage = 1): void
{
$demand = $this->createDemandObjectFromSettings($this->settings);
// ...
// Code from cart_products/Classes/Controller/ProductController.php listAction()
// ...
// instead of $products = $this->productRepository->findDemanded($demand);
if ($this->request->hasArgument('sorting')) {
$sorting = $this->request->getArgument('sorting');
$products = $this->productRepository->findDemandedSorted($demand,$sorting);
}
//...
?>
Then add you own repository function (extending class ProductRepository)
<?php
public function findDemandedSorted(ProductDemand $demand, $sortOrder)
{
$query = $this->createQuery();
// ...
// Code from cart_products/Classes/Domain/Repository/Product/ProductRepository.php findDemanded(ProductDemand $demand)
// ...
// instead of
// if ($orderings = $this->createOrderingsFromDemand($demand)) {
// $query->setOrderings($orderings);
// }
$query->setOrderings($sortOrder);
return $query->execute();
}

Upload multiple images using a many-to-many using Yii2

How to make loading multiple images and making many-to-many relationships on a table of apartments?
I have a model Apartment:
public function getApartmentImages()
{
return $this->hasMany(ApartmentImages::className(), ['apartment_id' => 'id']);
}
public function getImages()
{
return $this->hasMany(Images::className(), ['id' => 'image_id'])
->via('apartmentImages');
}
Model Images:
public function getApartmentImages()
{
return $this->hasMany(ApartmentImages::className(), ['image_id' => 'id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getApartments()
{
return $this->hasMany(Apartment::className(), ['id' => 'apartment_id'])
->via('apartmentImages');
}
Model ApartmentImages
public function getImage()
{
return $this->hasOne(Images::className(), ['id' => 'image_id']);
}
public function getApartment()
{
return $this->hasOne(Apartment::className(), ['id' => 'apartment_id']);
}
The apartment has a main image (layout) and many others.
I managed to make the layout load:
public function actionCreate()
{
$model = new Apartment();
$model->load(Yii::$app->getRequest()->getBodyParams(), '');
$layout = new Images();
$layout->imageFile = UploadedFile::getInstanceByName('layout');
if ($layout->upload()) {
$model->layout_id = $layout->id;
}
if ($model->validate()) {
if ($model->save()) {
$response = Yii::$app->getResponse();
$response->setStatusCode(201);
$id = implode(',', array_values($model->getPrimaryKey(true)));
$response->getHeaders()->set('Location', Url::toRoute(['view', 'id' => $id], true));
} elseif (!$model->hasErrors()) {
throw new ServerErrorHttpException('Failed to create the object for unknown reason.');
}
}
return $model;
}
I filled the array with all the sent pictures, and in the loop I upload to the site, and using
//INSERT INTO `apartment_images` (`apartment_id`, `image_id`) VALUES (...)
$model->link('images', $images[0]);
I create an entry in the link table.
public function actionCreate()
{
$model = new Apartment();
$model->load(Yii::$app->getRequest()->getBodyParams(), '');
$layout = new Images();
$layout->imageFile = UploadedFile::getInstanceByName('layout');
$layout->validate();
if ($layout->upload()) {
$model->layout_id = $layout->id;
}
$count = count(UploadedFile::getInstancesByName('images'));//Get the number of images
$images = [new Images()];//First image required
$images[0]->imageFile = UploadedFile::getInstanceByName('images[0]');
if(!$images[0]->validate()) return $images[0]->errors;//Check errors
if ($model->validate()){
if ($model->save()) {
if ($images[0]->upload()) {
$model->link('images',$images[0]);//Binding many to many
}
for($i = 1; $i < $count; $i++) {//Check the rest and repeat again
$images[$i] = new Images();
$images[$i]->imageFile = UploadedFile::getInstanceByName('images['.$i.']');
if ($images[$i]->upload()) {
$model->link('images',$images[$i]);
}
}
$response = Yii::$app->getResponse();
$response->setStatusCode(201);
$id = implode(',', array_values($model->getPrimaryKey(true)));
$response->getHeaders()->set('Location', Url::toRoute(['view', 'id' => $id], true));
} elseif (!$model->hasErrors()) {
throw new ServerErrorHttpException('Failed to create the object for unknown reason.');
}
}
else return $model->errors;
return $model;
}

Magento 2 category selector in system configuration

How to get category selector in system configuration in magento 2?
I need to get all levels of categories in the admin configuration section as multi select option. I have added the following code and I am getting desired result. But I want to do it using recursive function . Please help me to do the same.
namespace Vendor\Productslider\Model\Config\Source;
use Magento\Framework\Option\ArrayInterface;
use Magento\Catalog\Helper\Category;
class Categorylist implements ArrayInterface
{
protected $_categoryHelper;
protected $categoryRepository;
public function __construct(
\Magento\Catalog\Helper\Category $catalogCategory,
\Magento\Catalog\Model\CategoryRepository $categoryRepository
)
{
$this->_categoryHelper = $catalogCategory;
$this->categoryRepository = $categoryRepository;
}
/*
* Return categories helper
*/
public function getStoreCategories($sorted = false, $asCollection = false, $toLoad = true)
{
return $this->_categoryHelper->getStoreCategories($sorted , $asCollection, $toLoad);
}
/*
* Option getter
* #return array
*/
public function toOptionArray()
{
$arr = $this->toArray();
$ret = [];
foreach ($arr as $key => $value)
{
$ret[] = [
'value' => $key,
'label' => $value
];
}
return $ret;
}
/*
* Get options in "key-value" format
* #return array
*/
public function toArray()
{
$categories = $this->getStoreCategories(true,false,true);
$categoryList = $this->renderCategories($categories);
return $categoryList;
}
public function renderCategories($_categories)
{
$categoryList = array();
foreach ($_categories as $category){
$categoryList[$category->getEntityId()] = __($category->getName()); // Main categories
$categoryObj = $this->categoryRepository->get($category->getId());
$subcategories = $categoryObj->getChildrenCategories();
foreach($subcategories as $subcategory) {
$categoryList[$subcategory->getEntityId()] = __('--'.$subcategory->getName()); // 1st level Sub categories
if($subcategory->hasChildren()) {
$childCategoryObj = $this->categoryRepository->get($subcategory->getId());
$childSubcategories = $childCategoryObj->getChildrenCategories();
foreach($childSubcategories as $childSubcategory) {
$categoryList[$childSubcategory->getEntityId()] = __('----'.$childSubcategory->getName()); // 2nd level Sub categories
}
}
}
}
return $categoryList;
}
}
Thank God !!
I have find a solution
namespace Vendor\Productslider\Model\Config\Source;
use Magento\Framework\Option\ArrayInterface;
use Magento\Catalog\Helper\Category;
class Categorylist implements ArrayInterface
{
protected $_categoryHelper;
protected $categoryRepository;
protected $categoryList;
public function __construct(
\Magento\Catalog\Helper\Category $catalogCategory,
\Magento\Catalog\Model\CategoryRepository $categoryRepository
)
{
$this->_categoryHelper = $catalogCategory;
$this->categoryRepository = $categoryRepository;
}
/*
* Return categories helper
*/
public function getStoreCategories($sorted = false, $asCollection = false, $toLoad = true)
{
return $this->_categoryHelper->getStoreCategories($sorted , $asCollection, $toLoad);
}
/*
* Option getter
* #return array
*/
public function toOptionArray()
{
$arr = $this->toArray();
$ret = [];
foreach ($arr as $key => $value)
{
$ret[] = [
'value' => $key,
'label' => $value
];
}
return $ret;
}
/*
* Get options in "key-value" format
* #return array
*/
public function toArray()
{
$categories = $this->getStoreCategories(true,false,true);
$categoryList = $this->renderCategories($categories);
return $categoryList;
}
public function renderCategories($_categories)
{
foreach ($_categories as $category){
$i = 0;
$this->categoryList[$category->getEntityId()] = __($category->getName()); // Main categories
$list = $this->renderSubCat($category,$i);
}
return $this->categoryList;
}
public function renderSubCat($cat,$j){
$categoryObj = $this->categoryRepository->get($cat->getId());
$level = $categoryObj->getLevel();
$arrow = str_repeat("---", $level-1);
$subcategories = $categoryObj->getChildrenCategories();
foreach($subcategories as $subcategory) {
$this->categoryList[$subcategory->getEntityId()] = __($arrow.$subcategory->getName());
if($subcategory->hasChildren()) {
$this->renderSubCat($subcategory,$j);
}
}
return $this->categoryList;
}
}

remove item from cart for a given product id

I am new in magento i have tried to remove items in cart when call this event checkout_cart_product_add_after when i try this code nothing can doing. any body help me. thanks.
$myProductId=20;
$product = Mage::getModel('catalog/product')->setStoreId(Mage::app()->getStore()->getId())->load($myProductId);
$quote = Mage::getSingleton('checkout/session')->getQuote();
$cartItems = $quote->getItemByProduct($product);
if ($cartItems) { $quote->removeItem($cartItems->getId())->save();}
The ItemId (ID of an item in the cart) is not the same as the ProductId of the product it represents. Try iterating through the items in the cart until you find the one with the ProductId you want to remove:
$cartHelper = Mage::helper('checkout/cart');
$items = $cartHelper->getCart()->getItems();
foreach ($items as $item) {
if ($item->getProduct()->getId() == $productId) {
$itemId = $item->getItemId();
$cartHelper->getCart()->removeItem($itemId)->save();
break;
}
}
Please try as described above.
Below code work for me you can try this you can call this function using ajax or post method put this function inside your controller and call it. pass the customer id and product it to it
public function removeCartAction()
{
$productId = trim($_POST['productId']);
$customer = trim($_POST['requesterId']);
if ($customer) {
$storeId = Mage::app()->getWebsite(true)->getDefaultGroup()->getDefaultStoreId();
// get quote table cart detail of all customer added
$quote = Mage::getModel('sales/quote')->setStoreId($storeId)->loadByCustomer($customer);
if ($quote) {
$collection = $quote->getItemsCollection(false);
if ($collection->count() > 0) {
foreach( $collection as $item ) {
if ($item && $item->getId()) {
$quote->removeItem($item->getId());
$quote->collectTotals()->save();
}
}
}
}
}
}
To remove item by specific item_id from cart(quote) you can use this:
$cart = Mage::getModel('checkout/session')->getQuote();
$cartHelper = Mage::helper('checkout/cart');
$items = $cart->getAllVisibleItems();
foreach($items as $item):
if($item->getItemId() == $id):
$itemId = $item->getItemId();
$cartHelper->getCart()->removeItem($itemId)->save();
break;
endif;
endforeach;
Execute this you will get the output
$product = $observer->getEvent()->getProduct();
$cart = Mage::getSingleton('checkout/cart');
foreach ($cart->getQuote()->getItemsCollection() as $_item) {
if ($_item->getProductId() == $productId) {
$_item->isDeleted(true);
//Mage::getSingleton('core/session')->addNotice('This product cannot be added to shopping cart.');
}
}

concatenate fields with zend_form

I'm using the zend framework with centurion and I'm having a problem with my form. I have fields num_ordre and code, both of which are primary keys and I have columns in my table named conca, it's the concatenation of two fields, num_ordre and code.
My question is, in my method post, I want to test if the concatanation of num_ordre and code already exists in my database; but the problem is how to take a value of to fields before posting it.
This is my code
public function postAction(){
$this->_helper->viewRenderer->setNoRender(TRUE);
$user = new Param_Model_DbTable_Verification();
$form= $this->_getForm();
$form->getElement('Num_ordre')->addValidator(new Zend_Validate_Db_NoRecordExists('verifications','Num_ordre'));
$form->getElement('Num_ordre')->setRequired(true);
$posts = $this->_request->getPost();
if ($this->getRequest()->isPost()) {
$formData = $this->getRequest()->getPost();
if ($form->isValid($formData)) {
$row=$user->createRow();
$row->code=$this->_getParam('code');
$row->Num_ordre=$this->_getParam('Num_ordre');
$row->Libelle_champ=$this->_getParam('Libelle_champ');
$row->comparaison=$this->_getParam('comparaison');
$row->formule=$this->_getParam('formule');
$row->obligatoire=$this->_getParam('obligatoire');
$row->Req_traduction=$this->_getParam('Req_traduction');
$row->tolerance_erreur=$this->_getParam('tolerance_erreur');
$row->Mess_erreur=$this->_getParam('Mess_erreur');
$row->conca=$this->_getParam('Num_ordre').$this->_getParam('code');
$row->save();
if( isset ($posts['_addanother'])){
$_form = $this->_getForm();
$_form->removeElement('id');
$this->_helper->redirector('new','admin-verification');
}
else
$this->_helper->redirector(array('controller'=>'Admin-verification'));
}else{
parent::postAction();
}
}}
How about you just check it like this ?
public function postAction(){
$this->_helper->viewRenderer->setNoRender(TRUE);
$user = new Param_Model_DbTable_Verification();
$form= $this->_getForm();
$form->getElement('Num_ordre')->addValidator(new Zend_Validate_Db_NoRecordExists('verifications','Num_ordre'));
$form->getElement('Num_ordre')->setRequired(true);
$posts = $this->_request->getPost();
if ($this->getRequest()->isPost()) {
$formData = $this->getRequest()->getPost();
$mdl = new Model_Something(); //Call your model so you can test it
//Add a condition here
if ($form->isValid($formData) && $mdl->uniqueConcatenated($this->_getParam('num_ordre'), $this->_getParam('code')) {
$row=$user->createRow();
/**truncated, keep your existing code here**/
}
}
}
Then in your model Model_Something
public function uniqueConcatenated($numOrder, $code) {
$concatenated = $numOrder.$code;
//Check for the existence of a row with the concatenated field values
$select = $this->select();
$select->where('concatenatedField = '.$concatenated);
$row = $this->fetchRow($select);
return $row;
}
Hope this helps
You could manually call isValid on the validator:
$formData = $this->getRequest()->getPost();
if ($form->isValid($formData)) {
$formValues = $form->getValues();
$uniqueValidator = new Zend_Validate_Db_NoRecordExists('verifications','conca');
if ($uniqueValidator->isValid($formValues['Num_ordre'] . $formValues['Num_ordre'])) {
// valid
} else {
// not unique
}
}
untested code