#StackBounty: #magento2 #product #api Magento2 get Group wise Attribute collection for Attribute set in API

Bounty: 50

I want to list all product attributes group wise in admin by api.
If product sku is SKU001 and attribute set is 10.
I have separate API for attributes and attribute group

For all attribute within attributeset 10 is
/rest/V1/products/attribute-sets/10/attributes

And

For attributeset group :
rest/V1/products/attribute-sets/groups/list?searchCriteria[filterGroups][0][filters][0][value]=10&searchCriteria[filterGroups][0][filters][0][field]=attribute_set_id

I want to organize all attributes by group.All image attributes are show in Image tab,SEO attributes should be in SEO Tab etc are according to as it is in attribute set.I may be possible by add attribute_group_id field in attribute but dont know how i can achieve achieve it?


Get this bounty!!!

#StackBounty: #magento2 #product #category #orders #api Magento2 Copy data from one to another magento installation

Bounty: 50

I want to copy all data from one to another magento installation.

Wants to copy products,product-attributes,category,category-attributes,customers,cms-block,pages,rules everything except orders.
I can do it by take DB from one and setup it in new installation,but we have many modules and their data stores in DB and I’m not going to use so much modules for it.

So what will be best practice to achieve it without errors?
Old version : 2.2.2 to
latest version 2.3.2

Note : I don’t want it happen by Upgrading Magento version.


Get this bounty!!!

#StackBounty: #magento2 #api #nginx #angularjs How to enable CORs to access Magento rest APIs : Magento2

Bounty: 200

I’m getting an error while call API from Angular APP
test website and app is hosted in different domain.

  • Website Url abc.test.com
  • Angular App : xyz.test.com

When I call API for Listing Of product in getting below Error

Failed to load resource: the server responded with a status of 400 ()
Access to XMLHttpRequest at
https://abc.test.com/rest/V1/products?searchCriteria%5BpageSize%5D=10&searchCriteria%5BcurrentPage%5D=1&searchCriteria%5BsortOrders%5D%5B0%5D%5Bdirection%5D=DESC&searchCriteria%5BsortOrders%5D%5B0%5D%5Bfield%5D=created_at
from origin ‘http://xyz.test.com‘ has been blocked by CORS policy:
Response to preflight request doesn’t pass access control check: No
‘Access-Control-Allow-Origin’ header is present on the requested
resource.

How to solved it?


Get this bounty!!!

#StackBounty: #powershell #search #api Handling The SharePoint REST API Search Threshold Limit In powershell

Bounty: 50

We are using SharePoint Online. I would like to search in all sitecollections for the word “passport”. I make it working with the REST api in powershell. But I have now only the problem that it returns only the first 500 results. How can I iterate into all the batches of 500 results?

I found some blog posts how to that in spfx:

https://www.c-sharpcorner.com/article/handling-the-sharepoint-rest-api-search-threshold-limit-in-spfx/

This is my powershell code:

    try
{
    $searchKeyWord = "passport"

    $secpasswd = ConvertTo-SecureString "123123!" -AsPlainText -Force
    $mycreds = New-Object System.Management.Automation.PSCredential ("test@mycompany.nl", $secpasswd)

    # Connect to SharePoint Online
    $targetSite = "https://myCompany.sharepoint.com/"
    $targetSiteUri = [System.Uri]$targetSite

    Connect-PnPOnline $targetSite -Credentials $mycreds

    # Retrieve the client credentials and the related Authentication Cookies
    $context = (Get-PnPWeb).Context
    $credentials = $context.Credentials
    $authenticationCookies = $credentials.GetAuthenticationCookie($targetSiteUri, $true)

    # Set the Authentication Cookies and the Accept HTTP Header
    $webSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
    $webSession.Cookies.SetCookies($targetSiteUri, $authenticationCookies)
    $webSession.Headers.Add("Accept", "application/json;odata=verbose")

    # Set request variables
    $apiUrl = "$targetSite" + "_api/search/query?querytext='$searchKeyWord'&rowlimit=5000"#&rowlimit=$($rowLimit)&startrow=$($startRow)" #&selectproperties='Title,Author,Path'"

    # Make the REST request
    $webRequest = Invoke-WebRequest -Uri $apiUrl -Method Get -WebSession $webSession

    # Consume the JSON result
    $jsonLibrary = $webRequest.Content | ConvertFrom-Json
    $results = $jsonLibrary.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results

    #MaxRowLimit
    Write-Host $results.Count "Results" -ForegroundColor Green

    $ResultsArray = @()

    for($i=0; $i -le $results.length-1; $i++)
    {
        $row = $results[$i]
        $obj = New-Object PSObject

        for ($j=0; $j -le $row.Cells.results.length-1; $j++)
        {
            if ($row.Cells.results[$j].Key -eq ‘Title’)
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name Title -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘FileExtension’)
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name FileExtension -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘Path’)
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name Path -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘OriginalPath’)
            {
                Add-Member -InputObject $obj -MemberType NoteProperty  -Name OriginalPath -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘Author’) #
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name Author -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘HitHighlightedSummary’)
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name HitHighlightedSummary -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘SiteName’)
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name SiteName -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘SPWebUrl’)
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name SPWebUrl -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘IsDocument’)
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name IsDocument -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘ParentLink’)
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name ParentLink -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘ViewsLifeTime’) # aantal hits, niet unieke hits
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name ViewsLifeTime -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘ViewsRecent’) #
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name ViewsRecent -Value $row.Cells.results[$j].Value
            }

            if ($row.Cells.results[$j].Key -eq ‘Rank’) #
            {
                Add-Member -InputObject $obj -MemberType NoteProperty -Name Rank -Value $row.Cells.results[$j].Value
            }
        }

        $ResultsArray += $obj
    }

    $ResultsArray | Export-Csv -Path "c:outfile.csv" -NoTypeInformation
}
catch
{
    Write-Host $_.Exception.Message -ForegroundColor Red
}

Write-Host "Finished" -ForegroundColor Green


Get this bounty!!!

#StackBounty: #magento2 #api #rest-api #json #webapi Magento 2 WebAPI json encode

Bounty: 50

I’m returning JSON via an endpoint in Magento 2 this bit of JSON looks like the following:

    $new_json = [
        'component_1' => '[{....}]', 
        'component_2' => '[{...}]'
    ];
    return $new_json;

The endpoint works, but it returns the following:

0: "...."
1: "...."

Now I can pass the JSON into [ ] to return the following, but this isn’t what I want:

[0]:
component_1: "...."
component_2: "...."

My issue is I need to return the JSON with key and value, not 0 and 1 nor a sub value e.g. [0]

Is there a setting for the endpoint to stop it encoding the JSON like this? Or is there something I need to do to return it in the way that I want?

Thanks.


Get this bounty!!!

#StackBounty: #jquery #html #api #soap #wsdl Post a SOAP request in HTML and get response

Bounty: 50

I’m using WCFStorm in order to debug a very simple SOAP endpoint:

http://www.dneonline.com/calculator.asmx?WSDL

such endpoint can be used in order to request the 4 basic arithmetic calculations and retrieve a response: here in the example I’m requesting the numbers 3 and 5 and I receive as response 8, all good!

enter image description here

I now want to create an HTML page that can do the same without using WCFStorm. Here is my code:

<html>

<head>
    <title>Calling Web Service from jQuery</title>
    http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js
    
        $(document).ready(function() {
            $("#btnCallWebService").click(function(event) {
                var wsUrl = "http://www.dneonline.com/calculator.asmx?WSDL";
                var soapRequest = '      ' + $("#intA").val() + ' ' + $("#intB").val() + '   ';
                alert(soapRequest)
                $.ajax({
                    type: "POST",
                    url: wsUrl,
                    contentType: "text/xml",
                    dataType: "xml",
                    data: soapRequest,
                    success: processSuccess,
                    error: processError
                });

            });
        });

        function processSuccess(data, status, req) {
            alert('success');
            if (status == "success")
                $("#response").text($(req.responseXML).find("Int32").text());

            alert(req.responseXML);
        }

        function processError(data, status, req) {
            alert('err' + data.state);
            //alert(req.responseText + " " + status);
        }
    
</head>

<body>
    <h3>
        Calling Web Services with jQuery/AJAX
    </h3>
    Enter the numbers:
    <input id="intA" type="string" />
    <input id="intB" type="string" />
    <input id="btnCallWebService" value="Call web service" type="button" />
    
</body> </html>

But unfortunately I can only receive an: errundefined.
Where am I wrong?


Get this bounty!!!

#StackBounty: #api #.net-core #swashbuckle How to upload multipart file from request body in swashbuckle?

Bounty: 50

How should I setup swashbuckle in .net core so that I could upload file from request body (multipart, form value model binding disabled)?

What I tried (added FileUploadHelper in swashbuckle startup config, obviously):

public class FileUploadHelper : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        if (operation.OperationId.ToLower() == "apifileuploadpost")
        {
            operation.Parameters.Clear();
            operation.Parameters.Add(new NonBodyParameter()
            {
                Name = "File",
                In = "formData",
                Description = "uploaded file",
                Type = "file",
                Required = true,
            });
            operation.Consumes.Add("multipart/form-data");
        }
    }
}


 [HttpPost, DisableRequestSizeLimit]
 [DisableFormValueModelBinding]      
    public async Task<IActionResult> Upload()
    {
        int id = await Request.StreamFile(fileService);
        return CreatedAtAction(nameof(GetFileInfo), id);
    }

StreamFile is an extension method that saves file content from request body in filecontent entity (byte[]) and also creates a fileinfo entity with file content id and some additional info (name, description etc.) but returns only generated id. I just want to be able to click upload button in swagger, choose file and get the returned id or error response. I do NOT use IFileForm, form value model binding is disabled (according to large file upload using streaming in asp.net core documentation) and the file is straight from request body so I don’t pass any file-related parameter in Upload controller method, only ‘this HttpRequest’. Is that even doable in Swashbuckle?


Get this bounty!!!

#StackBounty: #php #json #wordpress #api #url-rewriting WordPress – How does one make a URL return dynamic JSON with custom Content Type?

Bounty: 50

Some background first, I’m trying to follow the directions here:

https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW1

to get universal links working with my website for iOS devices. My wordpress site is in a shared hosting environment, therefore I do NOT have access to the server or any root files. I placed the file at the root of my site (httpdocs) and when I navigate to the page it does not load. In googling around, I found a stackoverflow post stating you had to config the server to let it know to serve up that file with a content type of “application/pkcs7-mime” (Config apple-app-site-association file with wordpress). As mentioned I do not have access to that.

I have a subdomain which is a microsoft web api project that I am very familiar with. I made an endpoint for “apple-app-site-association” and dynamically built the json response in code and set the content type and returned it. This ended up working and Apple detects it and everything. This is interesting as I do NOT need a file on my server as I generate it on the fly, and I don’t need to change anything server wise.

The issue however is I need the main site domain to be the universal link, not the windows subdomain. The main site is wordpress. I’m thinking if may be possible to do the same thing in wordpress where I navigate to mydomain.com/apple-app-site-association and I generate the json and content-type on the fly and serve it up.

I’ve never done any coding with wordpress and don’t even know where to start.

If possible I would like clear direction in the following:

Making mydomain.com/apple-app-site-association lead to a custom function or page that serves up json.
Have code that makes the json get served up dynamically.
Have code that sets the content-type of the response to “application/pkcs7-mime”.
Of course if someone knows how to solve the file not being served no a shared server without this method, I’m open to that as well. From what I can see, this may be the only way assuming this can be done.


Get this bounty!!!

#StackBounty: #java #json #api #plivo error : use 'application/json' Content-Type and raw POST with json data

Bounty: 50

The error message

error : use ‘application/json’ Content-Type and raw POST with json
data

is being appeared from cyclos application inside plivo console which a third API that need to be integrated in cyclos through

Gateway URL: https://api.plivo.com/v1/Account/auth-id/Message/

HTTP username:xxx

HTTP password: **** 

HTTP headers

{
      'content-type': 'application/json',
}

HTTP request type: POST

HTTP request POST body

{
    "src":"+xxxx",
    "dst":"+xxx",
    "text":"some test"
}


Get this bounty!!!

#StackBounty: #php #interview-questions #api #rest #symfony3 Build a set of REST interfaces using Symfony framework

Bounty: 500

I have got a technical challenge from a company but they reject my code and I’m not sure how to do this challenge in a more efficient way so I’m here to get some guidance.

The Technical challenge was:

Manage a list of products that have prices.

  • Enable the administrator to set concrete prices (such as 10EUR) and discounts to prices either by a concrete amount (-1 EUR) or by
    percentage (-10%).
  • Enable the administrator to group products together to form bundles (which is also a product) that have independent prices.
  • Enable customers to get the list of products and respective prices.
  • Enable customers to place an order for one or more products, and provide customers with the list of products and the total price.

I have used Symfony framework to build this API and I’m writing the company response here:

  • SingleProduct and BundleProduct should be polymorphic.
  • ConcretePrice, DiscountedPriceByAmount, DiscountedPriceByPercentage should be polymorphic.
  • The computation of the overall sum of prices for the order should make no assumption about how the individual price was calculated (fix
    price, or discounted).
  • The response should provide a deep object structure (as opposed to a flat list) that preserves the semantics of the model and is suitable
    for rendering.

/src/Entity/Product.php

<?php 

// src/Entity/Product.php

namespace AppEntity;

use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;


/**
 * @ORMEntity(repositoryClass="AppRepositoryProductRepository")
 * @ORMTable(name="products")
 */
class Product {
    /**
     * @ORMColumn(type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
    * @AssertNotBlank()
    * @ORMColumn(type="string", length=150)
    */
    private $title;
    /**
     * @ORMColumn(type="text", length=150, nullable=true)
     */
    private $slug;
    /**
    * @AssertNotBlank()
    * @ORMColumn(type="float", scale=2)
    */
    private $price;
    /**
    * @ORMColumn(type="string", length=5)
    */
    private $currency = '€';
    /**
    * @ORMColumn(type="string", length=3, options={"comment":"Yes, No"})
    */
    private $isDiscount = 'No';
    /**
    * @ORMColumn(type="string", length=10, options={"comment":"Concrete amount (-1 EUR) or by Percentage (-10%)"}, nullable=true)
    */
    private $discountType;
    /**
    * @ORMColumn(type="integer", length=5, options={"comment":"1 or 10"})
    */
    private $discount = 0;
    /**
    * @ORMColumn(type="string", length=5, options={"comment":"No, Yes, if yes then save product ids in product bundle items"})
    */
    private $isProductBundle = 'No';
    /**
     * @ORMColumn(type="text", length=150, nullable=true)
    */
    private $sku;
    /**
    * @ORMColumn(type="string", length=15, options={"comment":"Active or Pending , only Active products will display to customers"})
    */
    private $status = 'Active';
    /**
    * @ORMColumn(type="string", length=150, options={"comment":"Upload or Link of image"})
    */
    private $imageType = 'Link';
    /**
     * @ORMColumn(type="text")
     */
    private $image = 'https://via.placeholder.com/400x300.png';
    /**
     * @ORMColumn(type="text", nullable=true)
     */
    private $description;
    /**
     * @ORMColumn(type="datetime", nullable=true)
     */
    private $createdAt;
    /**
     * @ORMColumn(type="datetime", nullable=true)
     */
    private $updatedAt;


    //Getters and Setters


}

/src/Entity/ProductBundleItem.php

<?php 

// src/Entity/ProductBundleItem.php

namespace AppEntity;

use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;


/**
 * @ORMEntity(repositoryClass="AppRepositoryProductBundleItemRepository")
 * @ORMTable(name="product_bundle_items")
 */
class ProductBundleItem {
    /**
     * @ORMColumn(type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @ORMColumn(type="integer")
    */
    private $productBundleId;

    /**
    * @ORMColumn(type="integer")
    */
    private $productId;


    //Getters and Setters

/src/Repository/ProductRepository.php

<?php

namespace AppRepository;

use AppEntityProduct;
use DoctrineBundleDoctrineBundleRepositoryServiceEntityRepository;
use SymfonyBridgeDoctrineRegistryInterface;

/**
 * @method Product|null find($id, $lockMode = null, $lockVersion = null)
 * @method Product|null findOneBy(array $criteria, array $orderBy = null)
 * @method Product[]    findAll()
 * @method Product[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class ProductRepository extends ServiceEntityRepository
{
    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, Product::class);
    }

    public function findAllQueryBuilder()
    {
        return $this->createQueryBuilder('products');
    }

    // /**
    //  * @return Product[] Returns an array of Product objects
    //  */
    /*
    public function findByExampleField($value)
    {
        return $this->createQueryBuilder('t')
            ->andWhere('t.exampleField = :val')
            ->setParameter('val', $value)
            ->orderBy('t.id', 'ASC')
            ->setMaxResults(10)
            ->getQuery()
            ->getResult()
        ;
    }
    */

    /*
    public function findOneBySomeField($value): ?Product
    {
        return $this->createQueryBuilder('t')
            ->andWhere('t.exampleField = :val')
            ->setParameter('val', $value)
            ->getQuery()
            ->getOneOrNullResult()
        ;
    }
    */
}

/src/Repository/ProductBundleItemRepository.php

<?php

namespace AppRepository;

use AppEntityProductBundleItem;
use DoctrineBundleDoctrineBundleRepositoryServiceEntityRepository;
use SymfonyBridgeDoctrineRegistryInterface;

/**
 * @method ProductBundleItem|null find($id, $lockMode = null, $lockVersion = null)
 * @method ProductBundleItem|null findOneBy(array $criteria, array $orderBy = null)
 * @method ProductBundleItem[]    findAll()
 * @method ProductBundleItem[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class ProductBundleItemRepository extends ServiceEntityRepository
{
    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, ProductBundleItem::class);
    }

    public function findByProductBundleIdJoinedToProduct($productBundleId)
    {

        return $this->createQueryBuilder('pbi')
                ->select('p.id','p.title', 'p.price', 'p.currency')
                ->leftJoin('AppEntityProduct', 'p', 'WITH', 'p.id = pbi.productId')
                ->where('pbi.productBundleId = :productBundleIdParam')
                ->setParameter('productBundleIdParam', $productBundleId)
                ->getQuery()
                ->getResult();
    }


    // /**
    //  * @return ProductBundleItem[] Returns an array of ProductBundleItem objects
    //  */
    /*
    public function findByExampleField($value)
    {
        return $this->createQueryBuilder('t')
            ->andWhere('t.exampleField = :val')
            ->setParameter('val', $value)
            ->orderBy('t.id', 'ASC')
            ->setMaxResults(10)
            ->getQuery()
            ->getResult()
        ;
    }
    */

    /*
    public function findOneBySomeField($value): ?ProductBundleItem
    {
        return $this->createQueryBuilder('t')
            ->andWhere('t.exampleField = :val')
            ->setParameter('val', $value)
            ->getQuery()
            ->getOneOrNullResult()
        ;
    }
    */
}

/src/Controller/Api/ProductController.php

<?php

// src/Controller/Api/ProductController.php

namespace AppControllerApi;

use FOSRestBundleControllerAnnotations as Rest;
use FOSRestBundleControllerAbstractFOSRestController;
use FOSRestBundleViewView;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use AppServiceProductService;

class ProductController extends AbstractFOSRestController
{

    private $productService;

    public function __construct(ProductService $productService){

        $this->productService = $productService;
    }

    /**
     * Retrieves a collection of Product resource
     * @RestGet("/products")
     */

    public function getProducts(Request $request): View
    {

        $params['page'] = $request->query->getInt('page', 1);
        $params['limit'] = $request->query->getInt('limit', 10);

        $products = $this->productService->getProducts($params);

        return View::create($products, Response::HTTP_OK);
    }

    /**
     * Retrieves a Product resource
     * @RestGet("/products/{slug}")
     */

    public function getProduct(Request $request, $slug): View
    {
        $product = $this->productService->getProduct($slug);

        return View::create($product, Response::HTTP_OK);
    }

    /**
     * Creates an Product resource
     * @RestPost("/products")
     * @param Request $request
     * @return View
    */
    public function addProduct(Request $request): View
    {
        $user = $this->getUser();
        if(!in_array('ROLE_ADMIN',$user->getRoles())){
            return View::create([], Response::HTTP_UNAUTHORIZED);
        }
        $params = json_decode($request->getContent(), true);
        $product = $this->productService->addProduct($params);
        return View::create($product, Response::HTTP_OK);

    }

    /**
     * Creates an Product resource
     * @RestPut("/products/{id}")
     * @param Request $request
     * @return View
    */
    public function updateProduct(Request $request, $id): View
    {
        $user = $this->getUser();
        if(!in_array('ROLE_ADMIN',$user->getRoles())){
            return View::create([], Response::HTTP_UNAUTHORIZED);
        }
        $params = json_decode($request->getContent(), true);
        $product = $this->productService->updateProduct($params, $id);
        return View::create($product, Response::HTTP_OK);

    }

    /**
     * Removes the Product resource
     * @RestDelete("/products/{id}")
    */
    public function deleteProduct($id): View
    {
        $user = $this->getUser();
        if(!in_array('ROLE_ADMIN',$user->getRoles())){
            return View::create([], Response::HTTP_UNAUTHORIZED);
        }
        $this->productService->deleteProduct($id);
        return View::create([], Response::HTTP_NO_CONTENT);

    }
}

/src/Controller/Api/ProductBundleController.php

<?php

// src/Controller/Api/ProductController.php

namespace AppControllerApi;

use FOSRestBundleControllerAnnotations as Rest;
use FOSRestBundleControllerAbstractFOSRestController;
use FOSRestBundleViewView;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use AppServiceProductBundleService;

class ProductBundleController extends AbstractFOSRestController
{

    private $productBundleService;

    public function __construct(ProductBundleService $productBundleService){

        $this->productBundleService = $productBundleService;
    }

    /**
     * Retrieves a collection of Product bundle resource
     * @RestGet("/products-not-bundles")
     */

    public function getProductsIsNotBundles(): View
    {

        $products = $this->productBundleService->getProductsIsNotBundles();

        return View::create($products, Response::HTTP_OK);
    }


    /**
     * Retrieves a collection of Product bundle resource
     * @RestGet("/product-bundles")
     */

    public function getProductBundles(): View
    {

        $products = $this->productBundleService->getProducts();

        return View::create($products, Response::HTTP_OK);
    }

    /**
     * Retrieves a Product bundle resource
     * @RestGet("/product-bundles/{id}")
     */

    public function getProduct(Request $request, $id): View
    {
        $product = $this->productBundleService->getProduct($id);

        return View::create($product, Response::HTTP_OK);
    }

    /**
     * Creates an Product bundle resource
     * @RestPost("/product-bundles")
     * @param Request $request
     * @return View
    */
    public function addProduct(Request $request): View
    {
        $user = $this->getUser();
        if(!in_array('ROLE_ADMIN',$user->getRoles())){
            return View::create([], Response::HTTP_UNAUTHORIZED);
        }
        $params = json_decode($request->getContent(), true);
        $product = $this->productBundleService->addProduct($params);
        return View::create($product, Response::HTTP_OK);

    }

    /**
     * Update an Product bundle resource
     * @RestPut("/product-bundles/{id}")
     * @param Request $request
     * @return View
    */
    public function updateProduct(Request $request, $id): View
    {
        $user = $this->getUser();
        if(!in_array('ROLE_ADMIN',$user->getRoles())){
            return View::create([], Response::HTTP_UNAUTHORIZED);
        }
        $params = json_decode($request->getContent(), true);
        $product = $this->productBundleService->updateProduct($params, $id);
        return View::create($product, Response::HTTP_OK);

    }

    /**
     * Removes the Product bundle resource
     * @RestDelete("/product-bundles/{id}")
    */
    public function deleteProduct($id): View
    {
        $user = $this->getUser();
        if(!in_array('ROLE_ADMIN',$user->getRoles())){
            return View::create([], Response::HTTP_UNAUTHORIZED);
        }
        $this->productBundleService->deleteProduct($id);
        return View::create([], Response::HTTP_NO_CONTENT);

    }
}

/src/Service/ProductService.php

<?php

namespace AppService;

use DoctrineORMEntityManagerInterface;
use PagerfantaAdapterDoctrineORMAdapter;
use PagerfantaPagerfanta;
use AppRepositoryProductRepository;
use AppUtilsSlugger;
use AppEntityProduct;

final class ProductService
{
    /**
    * @var ProductRepository
    */
    private $productRepository;
    private $slugger;
    private $em;

    public function __construct(ProductRepository $productRepository, Slugger $slugger, EntityManagerInterface $em){

        $this->productRepository = $productRepository;
        $this->slugger = $slugger;
        $this->em = $em;
    }

    public function getProducts($params): ?array
    {
        $qb = $this->productRepository->findAllQueryBuilder();
        $adapter = new DoctrineORMAdapter($qb);
        $pagerfanta = new Pagerfanta($adapter);
        $pagerfanta->setMaxPerPage($params['limit']);
        $pagerfanta->setCurrentPage($params['page']);

        $products = [];
        foreach ($pagerfanta->getCurrentPageResults() as $result) {
            $products[] = $result;
        }

        $response =[
            'total' => $pagerfanta->getNbResults(),
            'count' => count($products),
            'products' => $products,
        ];
        return $response;


    }

    public function getProduct($slug){
        #Find by id
        //return $this->productRepository->find($id);
        #Or find by slug
        return $this->productRepository->findBy(['slug'=>$slug]);
    }

    public function addProduct($params){

        $product = new Product();
        foreach($params as $key=>$val){
            $property = 'set'.strtoupper($key);
            if(property_exists('AppEntityProduct',$key)){
                $product->$property($val);
            }
        }

        $slug = $this->slugger->slugify($product->getTitle());
        $product->setSlug($slug);
        $product->setCreatedAt(date("Y-m-d H:i:s"));
        $product->setUpdatedAt(date("Y-m-d H:i:s"));

        $this->em->persist($product);
        $this->em->flush();

        return $product;
    }

    public function updateProduct($params, $id){

        if(empty($id))
            return [];
        $product = $this->productRepository->find($id);
        if(!$product){
            return [];
        }
        foreach($params as $key=>$val){
            if($key=='id')
                continue;
            $property = 'set'.ucfirst($key);
            if(property_exists('AppEntityProduct',$key)){
                $product->$property($val);
            }
        }

        $slug = $this->slugger->slugify($product->getTitle());
        $product->setSlug($slug);
        $product->setUpdatedAt(date("Y-m-d H:i:s"));

        $this->em->persist($product);
        $this->em->flush();

        return $product;
    }

    public function deleteProduct($id){
        $product = $this->productRepository->find($id);
        if($product){
            $this->em->remove($product);
            $this->em->flush();
        }
    }
}

/src/Service/ProductBundleService.php

<?php

namespace AppService;

use DoctrineORMEntityManagerInterface;
use AppRepositoryProductRepository;
use AppRepositoryProductBundleItemRepository;
use AppUtilsSlugger;
use AppEntityProduct;
use AppEntityProductBundleItem;

final class ProductBundleService
{
    /**
    * @var ProductRepository
    */
    private $productRepository;
    private $productBundleItemRepository;
    private $slugger;
    private $em;


    public function __construct(ProductRepository $productRepository, Slugger $slugger, EntityManagerInterface $em, ProductBundleItemRepository $productBundleItemRepository){

        $this->productRepository = $productRepository;
        $this->productBundleItemRepository = $productBundleItemRepository;
        $this->slugger = $slugger;
        $this->em = $em;
    }

    public function getProductsIsNotBundles(): ?array
    {
        return $this->productRepository->findBy(['status'=>'Active', 'isProductBundle'=>'No']);

    }

    public function getProducts(): ?array
    {
        return $this->productRepository->findBy(['isProductBundle'=>'Yes'],['id'=>'DESC']);
    }

    public function getProduct($id){
        #Find by id
        //return $this->productRepository->find($id);
        #Or find by slug
        $product = $this->productRepository->findBy(['id'=>$id,'isProductBundle'=>'Yes']);

        $bunleItems = $this->productBundleItemRepository->findByProductBundleIdJoinedToProduct($product[0]->getId());

        $returnData['product'] = $product;
        $returnData['bunleItems'] = $bunleItems;
        return $returnData;
    }

    public function addProduct($params){

        $product = new Product();
        foreach($params as $key=>$val){
            $property = 'set'.strtoupper($key);
            if(property_exists('AppEntityProduct',$key)){
                $product->$property($val);
            }
        }

        $product->setIsProductBundle("Yes");

        $slug = $this->slugger->slugify($product->getTitle());
        $product->setSlug($slug);
        $product->setCreatedAt(date("Y-m-d H:i:s"));
        $product->setUpdatedAt(date("Y-m-d H:i:s"));

        $this->em->persist($product);
        $this->em->flush();

        $productsArr = $params['productsArr'];

        if(count($productsArr)>0){
            foreach($productsArr as $productId){
               $productBundleItem = new ProductBundleItem();
               $productBundleItem->setProductBundleId($product->getId());
                $productBundleItem->setProductId($productId);
                $this->em->persist($productBundleItem);
                $this->em->flush();
            }
        }
        $returnData['product'] = $product;
        $returnData['productsArr'] = $productsArr;
        return $returnData;
    }

    public function updateProduct($params, $id){

        if(empty($id))
            return [];
        $product = $this->productRepository->find($id);
        if(!$product){
            return [];
        }
        foreach($params as $key=>$val){
            if($key=='id')
                continue;
            $property = 'set'.ucfirst($key);
            if(property_exists('AppEntityProduct',$key)){
                $product->$property($val);
            }
        }

        $product->setIsProductBundle("Yes");
        $slug = $this->slugger->slugify($product->getTitle());
        $product->setSlug($slug);
        $product->setUpdatedAt(date("Y-m-d H:i:s"));

        $this->em->persist($product);
        $this->em->flush();

        $productsArr = $params['productsArr'];

        if(count($productsArr)>0){
            foreach($productsArr as $productId){

                $isExist = $this->productBundleItemRepository->findBy(['productId'=>$productId]);
                if(!$isExist){
                    $productBundleItem = new ProductBundleItem();
                    $productBundleItem->setProductBundleId($product->getId());
                    $productBundleItem->setProductId($productId);
                    $this->em->persist($productBundleItem);
                    $this->em->flush();
                }
            }
        }

        $returnData['product'] = $product;
        $returnData['productsArr'] = $productsArr;
        return $returnData;
    }

    public function deleteProduct($id){
        $product = $this->productRepository->find($id);
        if($product){
            $productBundleItems = $this->productBundleItemRepository->findBy(['productBundleId'=>$product->getId()]);
            $this->em->remove($product);

            foreach($productBundleItems as $item){
                $this->em->remove($item);
            }

            $this->em->flush();
        }
    }
}

The whole code can be view here


Get this bounty!!!