How to Get Child Product Ids from a Configurable product - magento2

I am trying to fetch all child product Ids from configurable product Ids.
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$product = $objectManager->create('\Magento\Catalog\Model\Product');
$storeManager = $this->_objectManager->get('Magento\Store\Model\StoreManagerInterface');
$currentStore = $storeManager->getStore();
$mediaUrl = $currentStore->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA);
$productURL = $mediaUrl.'catalog/product';
$collection = $this->_productCollectionFactory->create();
$productCollection = $collection->addAttributeToSelect('*')->addAttributeToFilter('type_id','configurable');
$vendor_product = array();
$productData = $product->getData();
foreach($productCollection as $prodObj){
$productData = array();
$product = $product->load($prodObj->getId());
$productTypeInstance = $product->getTypeInstance();
$usedProducts = $productTypeInstance->getUsedProducts($product);
foreach ($usedProducts as $child) {
$productData['childrenIds'][] = $child->getId();
}
I get Ids of my first configurable product in all the cases

<?php
namespace Vendor\Module\Block;
class ParentAndChilds extends \Magento\Framework\View\Element\Template
{
/**
* #var Context
*/
protected $context;
/**
* #var ProductRepositoryInterface
*/
protected $productRepository;
/**
* #var SearchCriteriaBuilder
*/
protected $searchCriteriaBuilder;
/**
* #var LinkManagementInterface
*/
protected $linkManagement;
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
\Magento\ConfigurableProduct\Api\LinkManagementInterface $linkManagement,
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
\Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder,
array $data = []
)
{
$this->linkManagement = $linkManagement;
$this->productRepository = $productRepository;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
parent::__construct($context, $data);
}
public function getParentsAndChilds()
{
$searchCriteria = $this->searchCriteriaBuilder
->addFilter('type_id', 'configurable')
->create();
$configurableProducts = $this->productRepository
->getList($searchCriteria);
$parentAndChildProducts = array();
foreach ($configurableProducts->getItems() as $configurableProduct) {
$childProducts = $this->linkManagement
->getChildren($configurableProduct->getSku());
foreach ($childProducts as $childProduct) {
$parentAndChildProducts[$configurableProduct->getId()][] = $childProduct->getId();
}
}
return $parentAndChildProducts;
}
}

To get child product Ids from a configurable product, you can use below code:
public function __construct(
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository
) {
$this->productRepository = $productRepository;
}
public function execute()
{
$_productId=57786;
$_product=$this->productRepository->getById($_productId);
$childIs=$_product->getExtensionAttributes()->getConfigurableProductLinks();
print_r($childIs);
}
Output looks like:
Array
(
[31981] => 31981
[31982] => 31982
[31983] => 31983
)
Hope it will help you.

$_children = $_product->getTypeInstance()->getUsedProducts($_product);
foreach ($_children as $child){
$childProducts[] = $child;
}
$_product is the configurable product object

You can use the below code for getting Child ID.
$product = $block->getProduct();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$configurable_product_id = $product->getId();
$configurableProduct = $objectManager->get('Magento\Catalog\Model\ProductRepository')->getById($configurable_product_id);
$children = $configurableProduct->getTypeInstance()->getUsedProducts($configurableProduct); $childIds = array();
foreach ($children as $child){
$childIds[] = $child->getId();
}
sort($childIds);
print_r($childIds);
This is for sample code but I suggest do not use $objectManager directly, you can use Block on Plugin Method to get Product Object.

To get all simple children including out of stock products use:
$children = $product
->getTypeInstance()
->getChildrenIds($product->getId());
print_r($children);

Related

Magento 2.4: create CSV File for each order placed

I am new to magento. I need to automatically create a csv file whenever an order is placed by the customer.
I am using the event "sales_order_place_after" but the code in the observer is not working.
Can anyone guide me how to generate the csv file successfully whenever the order gets placed.
i keep getting errors like: Object of class Magento\Sales\Model\Order\Address could not be converted to string
Class Order implements ObserverInterface
{
protected $_request;
protected $_order;
protected $_productRepository;
protected $_scopeConfig;
protected $_customer;
protected $_storemanager;
public function __construct(
\Magento\Framework\App\RequestInterface $request,
\Magento\Sales\Model\Order $order,
\Magento\Framework\App\Response\Http\FileFactory $fileFactory,
Filesystem $filesystem,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Customer\Model\Customer $customer,
\Magento\Store\Model\StoreManagerInterface $storemanager,
\Magento\Checkout\Model\Session $checkoutSession,
\Magento\Sales\Model\OrderFactory $orderFactory,
\Magento\Framework\ObjectManager\ObjectManager $objectManager,
\Psr\Log\LoggerInterface $logger,
\Magento\Catalog\Model\ProductFactory $productFactory,
\Magento\Catalog\Model\ProductRepository $productRepository
) {
$this->_scopeConfig = $scopeConfig;
$this->_customer = $customer;
$this->_storemanager = $storemanager;
$this->_request = $request;
$this->_order = $order;
$this->_fileFactory = $fileFactory;
$this->directory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
$this->_productRepository = $productRepository;
}
public function execute(\Magento\Framework\Event\Observer $observer) {
$order = $observer->getEvent()->getOrder();
$websiteID = $this->_storemanager->getStore()->getWebsiteId();
$headers = array( 'Customer Name', 'Customer Email', ' Phone','Shipping Address' ,'SKU','Quantity','Price','Total','Weight');
$name = strtotime('now');
$file = 'customorderexport/'.$name.'_detailed_orderexport.csv';
$this->directory->create('customorderexport');
$stream = $this->directory->openFile($file, 'w+');
$stream->lock();
$stream->writeCsv($headers);
// $orderdetail['Customer Name'] =
$orderdetail['Customer Email'] = $order->getCustomerEmail();
$orderdetail['Contact Phone'] = $order->getTelephone();
$orderdetail['Shipping Address'] = $order->getShippingAddress();
$items = $order->getAllItems();
foreach ($items as $item) {
$orderdetail['SKU'] = $item->getSKU();
$orderdetail['Quantity'] = $item->getQtyOrdered();
$orderdetail['Price'] = $item->getPrice();
$orderdetail['Total'] = $item->getGrandTotal();
$orderdetail['Weight'] = $item->getWeight();
$stream->writeCsv($orderdetail);
}
$stream->unlock();
$stream->close();
}
}
This is because
$order->getShippingAddress();
will return a OrderAddressInterface object. (Magento\Sales\Api\Data\OrderAddressInterface)
You could do following for example:
$shippingAddress = $order->getShippingAddress();
$orderdetail['Shipping Address'] = $shippingAddress->getStreet()[0];

Too many Redirects with Controller

When I try to define my routes with an ImagesController, I get a "site redirected you too many times" error when going to what should be the index. I'm stumped. Am I over-tired and not seeing something obvious?
web.php
Route::prefix('images')->group(function(){
Route::post('add-tag', 'ImagesController#addTag');
Route::get('test', 'ImagesController#index');
});
When I access /images/test, the controller responds correctly. When I have it defined as '' or / it redirects too much. I'm not setting them simultaneously.
Route::prefix('images')->group(function(){
Route::post('add-tag', 'ImagesController#addTag');
// like this:
Route::get('', 'ImagesController#index');
// or like the following:
Route::get('/', 'ImagesController#index');
});
I've even tried to define /images before the prefix group to no avail. images/test is fine in this case.
Route::get('images', 'ImagesController#index');
Route::prefix('images')->group(function(){
Route::post('add-tag', 'ImagesController#addTag');
Route::get('test', 'ImagesController#index');
});
ImagesController.php
namespace App\Http\Controllers;
use \Auth;
use App\Helpers;
use App\Image;
use App\Tag;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\Console\Helper\Helper;
class ImagesController extends Controller
{
private $sortBy = [
'created_at',
'updated_at',
'orig_name',
'orig_size'
];
private $defaultSortBy = 'created_at';
private $defaultOrder = 'desc';
/**
* #param Request $request
* #return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index(Request $request)
{
$sort = $request->get('sort', $this->defaultSortBy);
$sort = in_array($sort, $this->sortBy) ? $sort : 'created_at';
$order = $request->get('order', $this->defaultOrder);
$order = $order == 'desc' ? 'desc' : 'asc';
$images = Image::orderBy($sort, $order)
->whereQuestionable(0)
->wherePrivate(0)
->paginate(config('image.images_per_page'));
if ($images !== null && $sort !== $this->defaultSortBy) {
$images->appends(['sort' => $sort]);
}
if ($images !== null && $order !== $this->defaultOrder) {
$images->appends(['order' => $order]);
}
return view(
'images.index',
[
'images' => $images,
'sort' => $sort,
'order' => $order
]
);
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
//this function works correctly, but I suppose it may be interfering?
public function addTag(Request $request)
{
Log::debug(print_r($request->all(), true));
$error = '';
$uid = trim($request->input('uid', ''));
$newTag = Helpers::prepTag($request->input('tag', ''));
if (!Auth::check()){
$error .= 'You can only add tags to images if you are logged in.';
} else if ($uid === '' || $newTag === '') {
$error .= 'UID and tag cannot be empty';
} else {
$image = Image::whereUid($uid)->unQuestionable()->first();
$currentTags = [];
if (count($image->tags)){
foreach ($image->tags as $currentTag) {
$currentTags[$currentTag->id] = $currentTag->tag;
}
}
if ($image === null) {
$error .= "Image $uid was not found.";
} else {
$tag = Tag::firstOrNew(['tag'=>$newTag]);
if ($tag->ip == '') {
$tag->ip = $request->getClientIp();
$tag->user_id = Auth::check() ? Auth::user()->id : 5;
$tag->save();
}
//\Log::info(print_r($currentTags, true));
//\Log::info(print_r($tag, true));
$addTag = !(bool)isset($currentTags[$tag->id]);
$image->tags()->sync([$tag->id], false);
}
}
if ($request->ajax()) {
if ($error !== '') {
return response()->json(['message' => $error],400);
}
return response()->json(['tagId' => $tag->id, 'imageUid' => $image->uid, 'tag' => $tag->tag, 'isAdmin' => true, 'addTag' => $addTag]);
} else {
if ($error !== '') {
Helpers::alert(3, $error, 'Tag Not Added');
}
return redirect('/');
}
}
}
As I have a model named Images implicit binding was interfering with my defined routes for a controller with the name ImagesController, and applying the prefix /images. After changing the name in the routing to image this issue went away.

How can I get the TYPO3 "appearance" media images by using "ViewHelper"?

thanks for reading and answering...
I have some TYPO3 gridelements in my TYPO3 pagecontent.
Each gridelement has a tab "appearance", where I define an image file.
The database relation from tt_content to the media image are in sys_file_reference.
My question is how can I get this image file in my fluid template by using the ViewHelper and the uid of my gridelement?
I wrote an little ViewHelper (tested on 7.6, but should not need so much changes for 8.7) to get referenced images for an newsletter layout:
<?php
namespace Vendor\Extension\ViewHelpers;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Resource\FileReference;
use TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
class ImagesReferencesViewHelper extends AbstractViewHelper implements CompilableInterface
{
/**
* Iterates through elements of $each and renders child nodes
*
* #param int $uid The ID of the element
* #param string $table The Referenced table name
* #param string $fieldName The Referenced field name
* #param string $as The name of the iteration variable
* #param string $key The name of the variable to store the current array key
* #param boolean $reverse If enabled, the iterator will start with the last element and proceed reversely
* #param string $iteration The name of the variable to store iteration information (index, cycle, isFirst, isLast, isEven, isOdd)
* #param string $link The name of the variable to store link
*
* #return string Rendered string
* #api
*/
public function render($uid, $table = 'tt_content', $fieldName = 'assets', $as, $key = '', $reverse = false, $iteration = null, $link = null)
{
return self::renderStatic($this->arguments, $this->buildRenderChildrenClosure(), $this->renderingContext);
}
/**
* #param array $arguments
* #param \Closure $renderChildrenClosure
* #param \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
*
* #return string
* #throws \TYPO3\CMS\Fluid\Core\ViewHelper\Exception
*/
static public function renderStatic(array $arguments, \Closure $renderChildrenClosure, \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext)
{
$templateVariableContainer = $renderingContext->getTemplateVariableContainer();
if ($arguments['uid'] === null || $arguments['table'] === null || $arguments['fieldName'] === null) {
return '';
}
/** #var FileRepository $fileRepository */
$fileRepository = GeneralUtility::makeInstance(FileRepository::class);
$images = array();
try {
$images = $fileRepository->findByRelation($arguments['table'], $arguments['fieldName'], $arguments['uid']);
} catch (Exception $e) {
/** #var \TYPO3\CMS\Core\Log\Logger $logger */
$logger = GeneralUtility::makeInstance(LogManager::class)->getLogger();
$logger->warning('The file-reference with uid "' . $arguments['uid'] . '" could not be found and won\'t be included in frontend output');
}
if (is_object($images) && !$images instanceof \Traversable) {
throw new \TYPO3\CMS\Fluid\Core\ViewHelper\Exception('ImagesReferencesViewHelper only supports arrays and objects implementing \Traversable interface', 1248728393);
}
if ($arguments['reverse'] === true) {
// array_reverse only supports arrays
if (is_object($images)) {
$images = iterator_to_array($images);
}
$images = array_reverse($images);
}
$iterationData = array(
'index' => 0,
'cycle' => 1,
'total' => count($images)
);
$output = '';
foreach ($images as $keyValue => $singleElement) {
$templateVariableContainer->add($arguments['as'], $singleElement);
/** #var FileReference $singleElement */
if ($arguments['link'] !== null && $singleElement->getLink()) {
$link = $singleElement->getLink();
/** #var ContentObjectRenderer $contentObject */
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$newLink = $contentObject->typoLink_URL(array('parameter' => $link));
$templateVariableContainer->add($arguments['link'], $newLink);
}
if ($arguments['key'] !== '') {
$templateVariableContainer->add($arguments['key'], $keyValue);
}
if ($arguments['iteration'] !== null) {
$iterationData['isFirst'] = $iterationData['cycle'] === 1;
$iterationData['isLast'] = $iterationData['cycle'] === $iterationData['total'];
$iterationData['isEven'] = $iterationData['cycle'] % 2 === 0;
$iterationData['isOdd'] = !$iterationData['isEven'];
$templateVariableContainer->add($arguments['iteration'], $iterationData);
$iterationData['index']++;
$iterationData['cycle']++;
}
$output .= $renderChildrenClosure();
$templateVariableContainer->remove($arguments['as']);
if ($arguments['link'] !== null && $singleElement->getLink()) {
$templateVariableContainer->remove($arguments['link']);
}
if ($arguments['key'] !== '') {
$templateVariableContainer->remove($arguments['key']);
}
if ($arguments['iteration'] !== null) {
$templateVariableContainer->remove($arguments['iteration']);
}
}
return $output;
}
}

using Zend_Service_Ebay_Finding to retrieve sellers items

I wish to retrieve all sellers items using the findItemsAdvanced call of Zend_Service_Ebay_Finding API. I'm a bit confused as to how to use it? has anyone got a example of how this method works? I tried
$response = $finding->findItemsAdvanced('seller=<SELLERNAME>');
But gives me nothing?
Would appreciate any help
In the end I overloaded the Zend_Service_Ebay_Finding API and added 2 methods to grab me all the seller info. Maybe this will help anyone else with the same issue.
/**
* Finds items for a specific seller
* and a page
*
* #param string $seller
* #param int $page
* #return Zend_Service_Ebay_Finding_Response_Items
*/
public function sellerItems($seller, $page = 1){
// prepare options
$options = array('itemFilter(0).name' => 'Seller', 'itemFilter(0).value(0)' => $seller, 'paginationInput.entriesPerPage' => 100);
// do request
return $this->_findItems($options, 'findItemsAdvanced');
}
/**
* Finds items for a specific seller - iterates through pages
* and a page
*
* #param string $seller
* #return array
*/
public function getAllSellerItems($seller) {
$page1 = $this->sellerItems($seller);
$pages = $page1->paginationOutput->totalPages;
$items = $page1->searchResult->item;
$full = array();
foreach($items as $item) {
$full[] = $item;
}
if($pages > 1) {
for($i = 2;$i <= $pages; $i ++) {
$results = $this->sellerItems($seller, $i);
$items = $results->searchResult->item;
foreach($items as $item) {
$full[] = $item;
}
}
}
return $full;
}

Doctrine ODM (MongoDB) - Get a complete array of an object?

iv'e got a problem to receive a complete array (with all the data of the embedded childs collections and objects) of my document. My document looks exactly like this one:
use Doctrine\Common\Collections\ArrayCollection;
/** #Document(collection="user") */
class User {
/** #Id */
protected $id;
/** #String */
protected $firstname;
/** #String */
protected $lastname;
/** #EmbedMany(targetDocument="Email") */
protected $email;
/** #EmbedMany(targetDocument="Address") */
protected $address;
/** #EmbedMany(targetDocument="Subscription") */
protected $subscription;
/**
* Construct the user
*
* #param array $properties
* #throws User_Exception
*/
public function __construct(array $properties = array()) {
$this->email = new ArrayCollection();
$this->address = new ArrayCollection();
$this->subscription = new ArrayCollection();
foreach($properties as $name => $value){
$this->{$name} = $value;
}
}
...
I need a complete array of an embedded collection to output the whole data and render it by json. My query looks like this:
$query = $this->_dbContainer->getDocumentManager()->createQueryBuilder('User')->field('deletedAt')->exists(false);
$result = $query->field('id')->equals($id)->getQuery()->getSingleResult();
For example, if i call the toArray() function like this:
$array = $result->getSubscription()->toArray();
print_r($array);
Then the output ist just an array on top level:
[0] => Object Subscription...
[1] => Object Subscription...
...
How can i easily get an array like this?
[0] => array('subscriptionLabel' => 'value1', 'field' => 'value1', ...)
[1] => array('subscriptionLabel' => 'value2', 'field' => 'value2', ...)
...
Are there any best practises or maybe some missing helper scripts to prevent something ugly like this code (how to handle child -> child -> child szenarios? ugly -> ugly ugly -> ugly ugly ugly -> ...):
$example = array();
foreach($result->getSubscription() as $key => $subscription) {
$example[$key]['subscriptionLabel'] = $subscription->getSubscriptionLabel();
$example[$key]['field'] = $subscription->getField();
...
}
Thanks a lot,
Stephan
Damn simple answer! Just use ->hydrate(false) and it's done.
For find queries the results by
default are hydrated and you get
document objects back instead of
arrays. You can disable this and get
the raw results directly back from
mongo by using the hydrate(false)
method:
<?php
$users = $dm->createQueryBuilder('User')
->hydrate(false)
->getQuery()
->execute();
print_r($users);
I ran into this same need recently and solved it by creating a base class for all my entities with a toArray() function and JsonSerializable. It converts all nested references as well.
/**
* #ODM\MappedSuperclass
*/
abstract class BaseDocument implements \JsonSerializable
{
public function jsonSerialize() {
return $this->toArray();
}
public function toArray() {
$getter_names = get_class_methods(get_class($this));
$gettable_attributes = array();
foreach ($getter_names as $key => $funcName) {
if(substr($funcName, 0, 3) === 'get') {
$propName = strtolower(substr($funcName, 3, 1));
$propName .= substr($funcName, 4);
$value = $this->$funcName();
if (is_object($value) && get_class($value) == 'Doctrine\ODM\MongoDB\PersistentCollection') {
$values = array();
$collection = $value;
foreach ($collection as $obj) {
$values[] = $obj->toArray();
}
$gettable_attributes[$propName] = $values;
}
else {
$gettable_attributes[$propName] = $value;
}
}
}
return $gettable_attributes;
}
}
Now I can serialize the entity as an array or json string with json_encode($doc). Bam.
Tanks to Rooster242, you can even recursively apply toArray to embedded documents which themself extends BaseDocument by using the php is_subclass_of function :
/**
* #ODM\MappedSuperclass
*/
abstract class BaseDocument implements \JsonSerializable
{
public function jsonSerialize() {
return $this->toArray();
}
public function toArray() {
$getter_names = get_class_methods(get_class($this));
$gettable_attributes = array();
foreach ($getter_names as $key => $funcName) {
if(substr($funcName, 0, 3) === 'get') {
$propName = strtolower(substr($funcName, 3, 1));
$propName .= substr($funcName, 4);
$value = $this->$funcName();
if (is_object($value) && is_subclass_of($value,"BaseDocument")) {
$gettable_attributes[$propName] = $value->toArray();
} elseif (is_object($value) && get_class($value) == 'Doctrine\ODM\MongoDB\PersistentCollection') {
$values = array();
$collection = $value;
foreach ($collection as $obj) {
if (is_subclass_of($obj,"BaseDocument")) {
$values[] = $obj->toArray();
} else {
$values[] = $obj;
}
}
$gettable_attributes[$propName] = $values;
}
else {
$gettable_attributes[$propName] = $value;
}
}
}
return $gettable_attributes;
}
}
Just made this a bit more generic, works perfect. Just dont forget to extend it with your documents and embeds.
<?php
namespace App\Documents;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Doctrine\ODM\MongoDB\PersistentCollection;
/**
* #ODM\MappedSuperclass
*/
abstract class BaseDocument implements \JsonSerializable
{
/**
* #return array
*/
public function jsonSerialize()
{
return $this->toArray();
}
/**
* #return array
*/
public function toArray()
{
$getterNames = get_class_methods(get_class($this));
$gettableAttributes = [];
foreach ($getterNames as $funcName) {
if (substr($funcName, 0, 3) !== 'get') {
continue;
}
$propName = strtolower(substr($funcName, 3, 1));
$propName .= substr($funcName, 4);
$value = $this->$funcName();
$gettableAttributes[$propName] = $value;
if (is_object($value)) {
if ($value instanceof PersistentCollection) {
$values = [];
$collection = $value;
foreach ($collection as $obj) {
/** #var BaseDocument $obj */
if ($obj instanceof \JsonSerializable) {
$values[] = $obj->toArray();
} else {
$values[] = $obj;
}
}
$gettableAttributes[$propName] = $values;
} elseif ($value instanceof \JsonSerializable) {
/** #var BaseDocument $value */
$gettableAttributes[$propName] = $value->toArray();
}
}
}
return $gettableAttributes;
}
}