I am in very bad situation, I got the objectiveC classes from the sudzc website.
using "http://www.xxx.in/mstore/api/soap/?wsdl"
in "SDZMagentoServiceExample.m" class I get one method
[service call:self action:#selector(callHandler:) sessionId: #"" resourcePath: #"catalog_category.level" args: (id)args];
It always gives me parameter error like
"Invalid website code requested:" if I pass dictionary or array in the args.
Please help me, I am in very bad situation.
thanks in advance.
From
/**
* Catalog category api
*
* #category Mage
* #package Mage_Catalog
* #author Magento Core Team <core#magentocommerce.com>
*/
class Mage_Catalog_Model_Category_Api extends Mage_Catalog_Model_Api_Resource
{
Following code:
/**
* Retrieve level of categories for category/store view/website
*
* #param string|int|null $website
* #param string|int|null $store
* #param int|null $categoryId
* #return array
*/
public function level($website = null, $store = null, $categoryId = null)
{
So nor array, nor dictionary would be accepted. Only raw string or int value.
I will not be able to help you in Objective C code, but I can show you some light with PHP. You can try out this type of call:-
$proxy = new SoapClient('http://www.iphone5case.in/mstore/api/soap/?wsdl');
$sessionId = $proxy->login('apiUser', 'apiKey');
/**
* As defined in the "Manage Stores" section of Admin panel,
* where you need to use the specific Website Code and/or Store Code
*/
$websiteCode = null;
$storeCode = 'german';
// Parent Category ID
$parentCategoryId = 2;
$firstLevel = $proxy->call($sessionId, 'category.level', array($websiteCode, $storeCode, $parentCategoryId));
Now if you print this variable "$firstLevel", you will get your required output, from this Web Service API.
Also whenever you are using Magento SOAP API v1, then each of the arguments will need to be as an array element. In this case, following are the main parameters expected for this API call "category.level":-
Website Code or ID
Store View Code or ID
Parent Category ID
So you need to create an array, and put sequentially each of the above arguments as array elements, like:-
array(
$websiteCode,
$storeCode,
$parentCategoryId
)
Lastly, please make sure that you reference this article always, as you can get the usage of almost all the Web Service API methods here.
Hope it helps.
Related
I created a simple backend module for TYPO3 (7.6.15), with help of the ExtensionBuilder. The UserController and MediaController have a createAction, showAction and listAction. The PanelController just has the showAction which is the main view of the module and should look like this:
Now, I want to render the listActions from the other controllers in the template of the PanelController.showAction and I would like to do it in the template of the view (MyExt/Resources/Private/Templates/Panel/Show.html), if possible.
I appreciate all help in advance and wish everyone a nice day!
/**
* Redirects the request to another action and / or controller.
*
* #param string $actionName Name of the action to forward to
* #param string $controllerName Unqualified object name of the controller to forward to. If not specified, the current controller is used.
* #param string $extensionName Name of the extension containing the controller to forward to. If not specified, the current extension is assumed.
* #param array $arguments Arguments to pass to the target action
* #param integer $pageUid Target page uid. If NULL, the current page uid is used
* #param integer $delay (optional) The delay in seconds. Default is no delay.
* #param integer $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other"
*/
protected function redirect(
$actionName,
$controllerName = NULL,
$extensionName = NULL,
array $arguments = NULL,
$pageUid = NULL,
$delay = 0,
$statusCode = 303
)
You have to pass controller name and action name in redirect method to call action of another controller.
Please review above method that helps you.
or if you do it in template itself than may be you have to call viewhelper.
Okay, I haven't even realized for years that the question was still open. I simply loaded both external controllers to get all the data I needed. Then I passed the data to the correct view templates within the view itself, which were the views of the original list actions.
When using Extbase's "show" action:
<f:link.action action="show" arguments="{event : event}">
I would like to look up said event by a special column ('customID').
The actual TYPO3-uid should NOT appear in the URL (with or without RealURL).
The reason is that the data has been imported, and the "real" uid is 'customId'.
There's always #biesior's approach using f:link.page https://stackoverflow.com/a/26145125/160968 – but I thought I'd try it with the official way.
(how) is it possible to do that in extbase/fluid?
This is possible. Let's assume your model Event has a property customId. So you generate your link like this:
<f:link.action action="show" arguments="{event : event.customId}">
The link generated will have a queryString like this:
?tx_myext[event]=9999
The showAction generated by the Extension Builder expects that the UID of the event is passed. The PropertyMapper then fetches the object automatically and assigns it to the view:
/**
* action show
*
* #param \Your\Extension\Domain\Model\Event $event
* #return void
*/
public function showAction(\Your\Extension\Domain\Model\Event $event) {
$this->view->assign('event', $event);
}
But in your case you cannot fetch the object by UID because you passed the customId. So you need to fetch the object yourself:
/**
* action show
*
* #param integer $event
* #return void
*/
public function showAction($event) {
$event = $this->eventRepository->findOneByCustomId($event);
$this->view->assign('event', $event);
}
The annotation #param integer $event tells TYPO3 that the parameter is "just" an integer. You then call the magic method findOneByCustomId from your eventRepository. findOne indicates that you want exactly one Event object back (and not a QueryResult), while the ByCustomId that queries an existing property of your Event model.
Why not use realUrl with lookUpTable? See here: https://wiki.typo3.org/Realurl/manual#-.3ElookUpTable
I set a nested resource like this in my routes.php file :
Route::resource('channels','ChannelsController');
Route::resource('channels.posts','PostsController');
and so when i want to show all posts on a given channel I would get the channel id form the URI :
GET /channels/{channelId}/posts with the method :
// PostsController.php
/**
* Display a listing of the resource.
* GET channels/{channelId}/posts/
* #return Response
*/
public function index($channelId)
{
...
}
but when i want to POST, the channel id will not get passed to the store method
// PostsController.php
/**
* Store a newly created post whithin a channel
* POST channels/{channelId}/posts/
* #return Response
*/
public function store($channelId)
{
... // $channelId is not set
}
I know there's a solution, passing the data with a hidden field in the form, but it is not secure since anyone can edit it and post the wrong id.
Please let me know, if you have any solution.
Actually, I found the solution in Laravel documentation itself, here is it so that everyone can take advantage from :
Form::open(array('action' => array('Controller#method', $user->id)))
the variable $user->id is passed as argument to the method method, also this last one should recieve an argument as well, like so : method($userId)
Building a JSON response for an API type thing, to retrieve a specific set of data that includes a ManyToOne relationship in the entity for my entity that extends FOSUSerBundle's User entity (called Account in my case).
The problem is, the Account entity thats included as a field in the response, is wanted, but I dont want to include all of the password and role type stuff.
I've been browing the internet for a couple hours now, and I've followed many guides on this, and I've cleared my cache every single time, and to no avail; So here's where I ended up:
// app/config/config.yml
jms_serializer:
metadata:
auto_detection: true
directories:
FOSUserBundle:
namespace_prefix: "FOS\\UserBundle"
path: "%kernel.root_dir%/Resources/serializer/FOS"
I've for below I've tried User.Model.yml and Model.User.yml and User.Entity.yml as well in a vain thought that the file name actually matters
// app/Resources/serializer/FOS/Entity.User.yml
FOS\UserBundle\Model\User:
exclusion_policy: ALL
properties:
id:
expose: true
and what I get still looks like this:
{
"status":"ok",
"api_version":"1.0",
"code":200,
"data":{
"video":{
"id":1,
"published":true,
"visibility":true,
"title":"Megaman 2",
"slug":"megaman-2",
"summary":"A rap song about Megaman",
"description":"A rap song\r\nAbout megaman",
"youtube_id":"R6L9bUouDr8",
"date_published":"2014-07-02T14:09:26-0700",
"date_created":"2014-07-02T14:09:26-0700",
"date_updated":"2014-07-02T14:09:26-0700",
"author_id":3,
"author":{
"id":3,
"username":"kharrison",
"username_canonical":"kharrison",
"email":"(sorry private)",
"email_canonical":"(sorry, private)",
"enabled":true,
"salt":"(sorry, private)",
"password":"(sorry, private)",
"last_login":"2014-07-04T15:17:34-0700",
"locked":false,
"expired":false,
"roles":[
"ROLE_SUPER_ADMIN"
],
"credentials_expired":false,
"display_name":"Kyle Harrison",
"slug":"kyle-harrison",
"bio":"Test"
}
}
}
}
The "author" field, is my Account entity thats being run through the JMSSerializer
I want to exclude ALL of that, except the user ID, Display name, and slug.
And finally this is how the API works:
// My/Bundle/Controller/BaseAPIController.php
//......... other code
/**
* #param string $status
* #param integer $code
* #return Response
*/
public function render_api($status, $code)
{
$this->apiResponse->setStatus($status);
$this->apiResponse->setCode($code);
return new Response($this->apiResponse->serialize($this->get('jms_serializer')), $this->apiResponse->getCode(), ["Content-type"=>"application/json"]);
}
//............. other code
and finally, that calls this:
// My/Bundle/Models
class APIResponse {
protected $status;
protected $apiVersion;
protected $code;
protected $data;
public function __construct($apiVersion, $status = "OK", $code = 500)
{
$this->status = $status;
$this->code = $code;
$this->apiVersion = $apiVersion;
$this->data = [];
}
// ... getters and setters
/**
* #return mixed
*/
public function serialize($serializer) {
return $serializer->serialize($this, "json");
}
}
I've for below I've tried User.Model.yml and Model.User.yml and
User.Entity.yml as well in a vain thought that the file name actually
matters.
It does matter, actually. It's a concatenation of the namespace and class name. In this case, you're trying to configure the FOS\UserBundle\Model\User class, so the file name should be Model.User.yml. (FOS\UserBundle\ should be excluded from the file name, since you configured it as namespace_prefix in your config.yml)
Also make sure that your Account class doesn't re-declare (overwrite) the properties, as the serializer config only works if you configure it for the class that actually declares the properties.
Ok So, the actual answer, couldn't have been arrived to via the information I provided. But Nic's Answer did lead me towards the solution. The description of how the the serializer looks at and deciphers the config file lead me to the real problem at hand.
This is what I failed to show:
<?php
namespace [PRIVATE]\[PRIVATE]Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\VirtualProperty;
/**
* Account
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="[PRIVATE]\[PRIVATE]Bundle\Entity\AccountRepository")
*/
class Account extends BaseUser
{
The problem lays with the Alias I provided the FOS\UserBundle\Model\User namespace. I no longer remember why I wrote that that way. However, the moment I remove the Alias and rewrote the extends to resemble this instead:
<?php
namespace [PRIVATE]\[PRIVATE]Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\VirtualProperty;
/**
* Account
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="[PRIVATE]\[PRIVATE]Bundle\Entity\AccountRepository")
*/
class Account extends User
{
combined with the new correct filename from Nic's answer, the config based Exclusion policy for JMSSerializerBundle totally kicks in, and every instance of FOSUserBundle's items are now completely hidden, except for the fields I've now explicitly told it to expose.
This is exactly what I wanted :)
Thanks everyone for your help! Cheers
~k
I'm not sure it's the exact way you want it, more a way around:
way around 1: Select only the properties you want (via the entity manager) and then serialize the array obtained.
It's what I do with what I call my API (which is not a class as you but controllers)
I am prototyping a REST API in Symfony2 with FOSRestBundle using JMSSerializerBundle for entity serialization. With GET request I can use the ParamConverter functionality of SensioFrameworkExtraBundle to get an instance of an entity based on the id request parameter and when creating a new entity with POST request I can use the FOSRestBundle body converter to create a new instance of the entity based on the request data. But when I want to update an existing entity, using the FOSRestBundle converter gives an entity without id (even when the id is sent with the request data) so if I persist it, it will create a new entity. And using SensioFrameworkExtraBundle converter gives me the original entity without the new data so I would have to manually get the data from the request and call all the setter methods to update the entity data.
So my question is, what is the preferred way to handle this situation? Feels like there should be some way to handle this using the (de)serialization of the request data. Am I missing something related to the ParamConverter or JMS serializer that would handle this situation? I do realize that there are many ways to do this kind of things and none of them are right for every use case, just looking for something that fits this kind of rapid prototyping you can do by using the ParamConverter and minimal code required to be written in the controllers/services.
Here is an example of a controller with the GET and POST actions as described above:
namespace My\ExampleBundle\Controller;
use My\ExampleBundle\Entity\Entity;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\View\View;
class EntityController extends Controller
{
/**
* #Route("/{id}", requirements={"id" = "\d+"})
* #ParamConverter("entity", class="MyExampleBundle:Entity")
* #Method("GET")
* #Rest\View()
*/
public function getAction(Entity $entity)
{
return $entity;
}
/**
* #Route("/")
* #ParamConverter("entity", converter="fos_rest.request_body")
* #Method("POST")
* #Rest\View(statusCode=201)
*/
public function createAction(Entity $entity, ConstraintViolationListInterface $validationErrors)
{
// Handle validation errors
if (count($validationErrors) > 0) {
return View::create(
['errors' => $validationErrors],
Response::HTTP_BAD_REQUEST
);
}
return $this->get('my.entity.repository')->save($entity);
}
}
And in config.yml I have the following configuration for FOSRestBundle:
fos_rest:
param_fetcher_listener: true
body_converter:
enabled: true
validate: true
body_listener:
decoders:
json: fos_rest.decoder.jsontoform
format_listener:
rules:
- { path: ^/api/, priorities: ['json'], prefer_extension: false }
- { path: ^/, priorities: ['html'], prefer_extension: false }
view:
view_response_listener: force
If you are using PUT, according to REST, you should use a route for the update with the id of the entity in question in the route itself like /entity/{entity}. FOSRestBundle does it that way too.
In your case this should be something like:
/**
* #Route("/{entityId}", requirements={"entityId" = "\d+"})
* #ParamConverter("entity", converter="fos_rest.request_body")
* #Method("PUT")
* #Rest\View(statusCode=201)
*/
public function putAction($entityId, Entity $entity, ConstraintViolationListInterface $validationErrors)
EDIT: It would actually be even better to have two entities injected. One being the current database state and one being the sent data from the client. You can achieve this with two ParamConverter-annotations:
/**
* #Route("/{id}", requirements={"id" = "\d+"})
* #ParamConverter("entity")
* #ParamConverter("entityNew", converter="fos_rest.request_body")
* #Method("PUT")
* #Rest\View(statusCode=201)
*/
public function putAction(Entity $entity, Entity $entityNew, ConstraintViolationListInterface $validationErrors)
This will load the current db state into $entity and the uploaded data into $entityNew. Now you can merge the data as you see fit.
If it's fine for you to just overwrite the data without merging/checking, then use the first option. But keep in mind that this would allow creating a new entity if the client sends a not yet used id if you do not prevent that.
Seems one way would be to use Symfony Form component (with SimpleThingsFormSerializerBundle) as described in http://williamdurand.fr/2012/08/02/rest-apis-with-symfony2-the-right-way/#post-it
Quote from SimpleThingsFormSerializerBundle README:
Additionally all the current serializer components share a common flaw: They cannot deserialize (update) into existing object graphs. Updating object graphs is a problem the Form component already solves (perfectly!).
I also had a problem with the processing of PUT requests using JMS serializer. First of all I would like to automate the processing of queries using the serializer. The put request may not contain the complete data. Part of the data must be map on entity. You can use my simple solution:
/**
* #Route(path="/edit",name="your_route_name", methods={"PUT"})
*
* This parameter is using for creating a current fields of request
* #RequestParam(
* name="id",
* requirements="\d+",
* nullable=false,
* allowBlank=true,
* strict=true,
* )
* #RequestParam(
* name="some_field",
* requirements="\d{13}",
* nullable=true,
* allowBlank=true,
* strict=true,
* )
* #RequestParam(
* name="some_another_field",
* requirements="\d{13}",
* nullable=true,
* allowBlank=true,
* strict=true,
* )
* #param Request $request
* #param ParamFetcher $paramFetcher
* #return Response
*/
public function editAction(Request $request, ParamFetcher $paramFetcher)
{
//validate parameters
$paramFetcher->all();
/** #var EntityManager $em */
$em = $this->getDoctrine()->getManager();
$yourEntity = $em->getRepository('YourBundle:SomeEntity')->find($paramFetcher->get('id'));
//get request params (param fetcher has all params, but we need only params from request)
$data = $request->request->all();
$this->mapDataOnEntity($data, $yourEntity, ['some_serialized_group','another_group']);
$em->flush();
return new JsonResponse();
}
Method mapDataOnEntity you can locate in some trait or in you intermediate controller class. Here is his implementation of this method:
/**
* #param array $data
* #param object $targetEntity
* #param array $serializationGroups
*/
public function mapDataOnEntity($data, $targetEntity, $serializationGroups = [])
{
/** #var object $source */
$sourceEntity = $this->get('jms_serializer')
->deserialize(
json_encode($data),
get_class($targetEntity),
'json',
DeserializationContext::create()->setGroups($serializationGroups)
);
$this->fillProperties($data, $targetEntity, $sourceEntity);
}
/**
* #param array $params
* #param object $targetEntity
* #param object $sourceEntity
*/
protected function fillProperties($params, $targetEntity, $sourceEntity)
{
$propertyAccessor = new PropertyAccessor();
/** #var PropertyMetadata[] $propertyMetadata */
$propertyMetadata = $this->get('jms_serializer.metadata_factory')
->getMetadataForClass(get_class($sourceEntity))
->propertyMetadata;
foreach ($propertyMetadata as $realPropertyName => $data) {
$serializedPropertyName = $data->serializedName ?: $this->fromCamelCase($realPropertyName);
if (array_key_exists($serializedPropertyName, $params)) {
$newValue = $propertyAccessor->getValue($sourceEntity, $realPropertyName);
$propertyAccessor->setValue($targetEntity, $realPropertyName, $newValue);
}
}
}
/**
* #param string $input
* #return string
*/
protected function fromCamelCase($input)
{
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
}
return implode('_', $ret);
}
The best way is using JMSSerializerBundle
The problem is JMSSerializer initializes with the default ObjectConstructor for deserialization (setting the fields that are not in the request as null, and making that merge method will also persist null properties to database). So you need to switch this one with the DoctrineObjectConstructor.
services:
jms_serializer.object_constructor:
alias: jms_serializer.doctrine_object_constructor
public: false
Then just deserialize and persist the entity, and it will be filled with the missing fields. When you save to database only the attributes that have changed will be updated on the database:
$foo = $this->get('jms_serializer')->deserialize(
$request->getContent(),
'AppBundle\Entity\Foo',
'json');
$em = $this->getDoctrine()->getManager();
$em->persist($foo);
$em->flush();
Credits to: Symfony2 Doctrine2 De-Serialize and Merge Entity issue
I'm having the same issue as you described, I just do the entity merging manually:
public function patchMembersAction($memberId, Member $memberPatch)
{
return $this->members->updateMember($memberId, $memberPatch);
}
This calls method that does the validation, and then manually calls all the required setter methods. Anyway, I'm wondering about writing my own param converter for such cases.
Another resource which helped me a lot is http://welcometothebundle.com/symfony2-rest-api-the-best-2013-way/. A step by step tutorial which filled in the blanks I had after the resource in the previous comment. Good luck!