Shopware 6 : How to get dynamic url in custom plugin controller? - plugins

Shopware 6
I am new to shopware. I want base url in controller ( custom plugin ).
I tried this but it's not working for me.
$salesChannel->getDomains()->first()->getUrl();
Thanks in advance

If you just need the current host and base url in your controller:
use Shopware\Storefront\Framework\Routing\RequestTransformer;
use Symfony\Component\HttpFoundation\Request;
// ...
/**
* #Route(...)
*/
public function myAction(Request $request): Response
{
$host = $request->attributes->get(RequestTransformer::SALES_CHANNEL_ABSOLUTE_BASE_URL)
. $request->attributes->get(RequestTransformer::SALES_CHANNEL_BASE_URL);
// ...
}

First : Add this namespace
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
Second : here you can get base url.
$urls = [];
$salesChannelRepository = $this->container->get('sales_channel.repository');
$criteria = new Criteria();
$criteria->addAssociation('domains');
$salesChannelIds = $salesChannelRepository->search($criteria, Context::createDefaultContext());
foreach($salesChannelIds->getEntities()->getElements() as $key => $salesChannel){
foreach($salesChannel->getDomains()->getElements() as $element){
array_push($urls, $element->getUrl());
}
}
you will get your url in $urls variable.

If you have access to the request object which you should have in a controller, you have access to an request attribute called sw-storefront-url. This will give you exactly what you need: the base-url of the current request.
See also How to get the current saleschannel domain in Shopware 6 storefront/TWIG?

Related

Can i write single method in CodeIgniter Controller to handle both GET and POST login form?

In web application we are using POST and in IPAD we are using GET to submit form details. But I written both GET and POST method in my CodeIgniter Controller. But it's not good because I'm maintaining same code twice in Cotroller?
Current Code:
class LoginHandle extends REST_Controller
{
public function authenticate_post()
{}
public function authenticate_post()
{}
}
Is is possible some thing like following?
class LoginHandle extends REST_Controller<br>
{
public function authenticate_get_post()
{}
}
As I expect there aren't many form items to check - you could use (assuming PHP7+) the ?? operator like this:
$username = $_POST['User'] ?? $_GET['User'];
$password = $_POST['Pass'] ?? $_GET['Pass'];
For PHP < 7 you can use ...
$username = !empty($_POST['User']) ? $_POST['User'] : !empty($_GET['User']) ? $_GET['User'] : null;
$password = !empty($_POST['Pass']) ? $_POST['Pass'] : !empty($_GET['Pass']) ? $_GET['Pass'] : null;
Please, see: https://www.codeigniter.com/user_guide/libraries/input.html#CI_Input::post_get
$this->input->post_get('key')

Dingo Api response->created | location and content example

I am creating API with Laravel 5.2 and Dingo API package. When a user is created, I want to return 201 response with the new $user->id.
My Code
return $this->response->created();
As per Dingo documentatio, I can provide a location and $content as parameters in the created() function.
My question is, what location information I need to return here and I tried to set my new user as $content, but it's not working or I am not sure what to expect.
Can someone please explain this created() function?
What this does is set the Location header, as seen in the source:
/**
* Respond with a created response and associate a location if provided.
*
* #param null|string $location
*
* #return \Dingo\Api\Http\Response
*/
public function created($location = null, $content = null)
{
$response = new Response($content);
$response->setStatusCode(201);
if (! is_null($location)) {
$response->header('Location', $location);
}
return $response;
}
So, in your example since you're creating a new user, you might send the users profile page as the location, something like:
return $this->response->created('/users/123');
As for the content, as you can see in the function this sets the content on the return. In your case, it would probably be a json string with the new user information, something like:
return $this->response->created('/users/123', $user); // laravel should automatically json_encode the user object

How to test form request rules in Laravel 5?

I created a form request class and defined a bunch of rules. Now I would like to test these rules to see if the behaviour meets our expectations.
How could I write a test to accomplish that?
Many thanks in advance for your answers!
Update: more precisely, I would like to write a unit test that would check e.g. if a badly formatted email passes validation or not. The problem is that I don't know how to create a new instance of the Request with fake input in it.
The accepted answer tests both authorization and validation simultaneously. If you want to test these function separately then you can do this:
test rules():
$attributes = ['aa' => 'asd'];
$request = new MyRequest();
$rules = $request->rules();
$validator = Validator::make($attributes, $rules);
$fails = $validator->fails();
$this->assertEquals(false, $fails);
test authorize():
$user = factory(User::class)->create();
$this->actingAs($user);
$request = new MyRequest();
$request->setContainer($this->app);
$attributes = ['aa' => 'asd'];
$request->initialize([], $attributes);
$this->app->instance('request', $request);
$authorized = $request->authorize();
$this->assertEquals(true, $authorized);
You should create some helper methods in base class to keep the tests DRY.
You need to have your form request class in the controller function, for example
public function store(MyRequest $request)
Now create HTML form and try to fill it with different values. If validation fails then you will get messages in session, if it succeeds then you get into the controller function.
When Unit testing then call the url and add the values for testing as array. Laravel doc says it can be done as
$response = $this->call($method, $uri, $parameters, $cookies, $files, $server, $content);
Here's a full example of testing validation:
use App\Http\Requests\PostRequest;
use Illuminate\Routing\Redirector;
use Illuminate\Validation\ValidationException;
class PostRequestTest extends TestCase
{
protected function newTestRequest($data = [])
{
$request = new PostRequest();
$request->initialize($data);
return $request
->setContainer($this->app)
->setRedirector($this->app->make(Redirector::class));
}
public function testValidationFailsWhenEmptyTitleIsGiven()
{
$this->expectException(ValidationException::class);
$this->newTestRequest(['title' => ''])->validateWhenResolved();
}
}

How to get currentPagePath in Slingservlet?

From some javascript I call the following Slingservlet with ("/bin/fooServlet?"+params);
#SlingServlet(paths = "/bin/fooServlet", methods = "GET", metatype = true)
public class FooServlet extends SlingAllMethodsServlet {
..
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {
Session session = resourceResolver.adaptTo(Session.class);
Page currentPage = pageManager.getPage(request.getPathInfo());
String currentPagePath = currentPage.getPath();
...
}
My Question is: How to get the currentPagePath of the current Page in FooServlet? currentPagePath in the code is null.
As Thomas mentioned, if you define a servlet with fixed paths property, you wouldn't have reference to a Resource.
One way of achieving this is by passing your page path along with the request to the servlet. Also CQ.WCM.getPagePath() returns only /libs/wcm/core/content/siteadmin, as the current page is siteadmin and you may need to tweak your script a bit in order to access the selected page within siteadmin.
To get your page path either from siteadmin or from the page itself, you can use the following script and then pass the value to your servlet for further processing.
var currentPagePath = null;
/* if accessed via siteadmin */
if(CQ.wcm.SiteAdmin.hasListSelection()) {
var grid = CQ.wcm.SiteAdmin.getActiveGrid();
var selections = grid.getSelectionModel().getSelections();
/*Assuming that you are selecting only one page at a time. */
currentPagePath = selections[0].id;
} else { /* accessed via page */
currentPagePath = CQ.WCM.getPagePath();
}
And then you can call the servlet with the currentPagePath as one of the parameters.
GET /bin/fooServlet?currentPagePath=' + currentPagePath + '&foo=bar';
UPDATE
The above code works fine for CQ 5.5 + , for older versions you can use this.
var currentPagePath = null;
/* if accessed via siteadmin */
if(CQ.wcm.SiteAdmin.hasListSelection()) {
var grid = CQ.Ext.getCmp(window.CQ_SiteAdmin_id + "-grid");
if (grid) {
var selections = grid.getSelectionModel().getSelections();
currentPagePath = selections[0].id;
}
} else { /* accessed via page */
currentPagePath = CQ.WCM.getPagePath();
}
If you define the servlet with a fixed paths property you don't have any reference to a Resource or Page
You either need to define resourceTypes that matches to a page component or use cq:Page, but this will then be active for every request to a page and is not recommended without at least some selectors
Then you can get the Resource with request.getResource(). To get a Page you'll need to adapt the ResourceResolver to a PageManager and use getContainingPage(Resource resource).
Have a look at the documentation:
http://sling.apache.org/documentation/the-sling-engine/servlets.html
request.getPathInfo() in this case presumably is /bin/fooServlet?[parameterString], which is why PageManager is returning null for its path — from the PageManager's point of view, no resource exists at this location.
One simple option would be to send an additional callingPage parameter when hitting the Servlet. This way you could just read it from the parameter map:
GET /bin/fooServlet?foo=bar&callingPage=/en/home.html
void doGet() {
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
String callingPage = request.getParameter("callingPage");
String callingPagePath = pageManager.getPage(callingPage).getPath();
}
I don't know if it's a good pratice, but perhaps you could use the referer.
import java.net.URI;
import java.net.URISyntaxException;
try {
String currentPagePath = new URI(request.getHeader("referer")).getPath();
} catch (java.net.URISyntaxException e) {
}

Silverstripe - Restful Server Caching

for a web application (an Image Database) I am using the Restful Server Module. The Data is requested by another web application (a shop). The generation of the XML takes up to 1 second. the shop has to wait for the API to answer to display for example a Product Page. Is it possible to activate some caching for the Restful Server API?
I tried already with the Static Publisher but it seems to work just with cms pages.
Many thanks,
Florian
RestfulService does the caching for you. It accepts 2 paramaters. A serviceURL and also a cache time. The default is 3600 (1 hour). This is only going to work if the shop is built with silverstripe.
$serviceURL = 'http://www.imagedatabase.com/api/v1/Product?ID=1';
$service = new RestfulService($serviceURL, 7200); //2 hours expiry
$xml = $service->request()->getBody();
//get fields values
$productName = $service->searchValue($xml, 'Name');
$productPrice = $service->searchValue($xml, 'Price');
You also need to make a modification to Product assuming Product is a dataobject.
class Product extends DataObject {
...
static $api_access = true;
...
function canView($member = null) {
return true;
}
}
RestfulService docs
http://doc.silverstripe.org/framework/en/reference/restfulservice
well, I personally would cache on the client (so in the shop)
but if you have to, I don't think there is any built in way for this.
you could subclass the restful server and do some basic caching yourself (just the way the default SS RestfulClient does it, save it into a file)
class MyServer extends RestfulServer {
public $cache_expire;
function __construct($cache_expire = 3600) {
$this->cache_expire = $cache_expire;
}
protected function getHandler($className, $id, $relationName) {
$cache_path = Director::getAbsFile("assets/rest-cache/$className-$id-$relationName.{$this->request->getExtension()}");
if ($this->cache_expire > 0 && !isset($_GET['flush'])
&& #file_exists($cache_path) && #filemtime($cache_path) + $this->cache_expire > time()
) {
$store = file_get_contents($cache_path);
$response = unserialize($store);
} else {
$response = parent::getHandler($className, $id, $relationName);
$store = serialize($response);
file_put_contents($cache_path, $store);
}
return $response;
}
}
// NOTE, I have never tested this code, so you might run into minor spelling mistakes or something like that