Symfony - Add a callback if entity not found - api

I'm struggling with something for several days. I'm using API which stores data about food products.
What I would like to do is :
A user is looking for a product (using the bar code )
We search the product in the database
If nothing is found, then we reach the API to retrieve the information and we save them in the database
And then we process as usual with the loaded entity
Is there a way (event or other) to add a behavior in case of the entity does not exists in the DB ?
Thanks in advance

untested code to illustrated the flow.
in your repository:
public function findByEAN(string $ean): Product
{
$product = $this->findOnyBy(['ean' => $ean]);
if ($product === null) {
throw new EntityNotFoundException(sprintf('No Product with EAN %s', $ean);
}
return $product;
}
in your controller action:
try {
$product = $this->getEntityManager()->getRepository(Product::class)->findByEan($request->get('ean'));
} catch (EntityNotFoundException $e) {
$product = '' ; // fetch product via api and store in DB
}
return $product;

Related

Shopware 6 custom element type image not showing any data on storefront

I have created my component to add some desired config fields in Shopware 6. Everything is working fine but one problem that is image is looking as it is being saved in the administration but is not showing any src or else in dump.
And here is my dump preiew having #data null.
can anyone tell what should I do else here?
I will be very thankful.
There is a guide in the docs that explains exactly what your case is.
You can likely extend the \Shopware\Core\Content\Media\Cms\ImageCmsElementResolver and override the getType function:
public function getType(): string
{
return 'my-component-name';
}
The important part of the default ImageCmsElementResolver is the loading the media information. For that you also need in your CMS element resolver. I explain some parts of the existing ImageCmsElementResolver so you can see which steps you need:
public function collect(CmsSlotEntity $slot, ResolverContext $resolverContext): ?CriteriaCollection
{
// read the configuration, that is defined in the Admin JS. Likely also media for you
$mediaConfig = $slot->getFieldConfig()->get('media');
// if this config is NOT containing useful info
if (
$mediaConfig === null
|| $mediaConfig->isMapped()
|| $mediaConfig->isDefault()
|| $mediaConfig->getValue() === null
) {
// return nothing
return null;
}
// otherwise use the configured value as mediaId to load the media entry from the database
$criteria = new Criteria([$mediaConfig->getStringValue()]);
$criteriaCollection = new CriteriaCollection();
$criteriaCollection->add('media_' . $slot->getUniqueIdentifier(), MediaDefinition::class, $criteria);
// return the criterias to execute later, when all needed entities for the CMS page are fetched
return $criteriaCollection;
}
Now the data is fetched and as next step you need to put it into a variable accessible from the Twig template. For this you write into the same CMS element resolver this:
public function enrich(CmsSlotEntity $slot, ResolverContext $resolverContext, ElementDataCollection $result): void
{
$config = $slot->getFieldConfig();
$image = new ImageStruct();
// this is important for accessing data in Twig
$slot->setData($image);
// read the config again
$mediaConfig = $config->get('media');
// if the configuration looks promising
if ($mediaConfig && $config->isStatic() && $mediaConfig->getValue()) {
$image->setMediaId($config->getStringValue());
// look up the media from the entity loading step
$searchResult = $result->get('media_' . $slot->getUniqueIdentifier());
if (!$searchResult) {
return;
}
/** #var MediaEntity|null $media */
$media = $searchResult->get($config->getValue());
// if we do not have a media, then skip it
if (!$media) {
return;
}
// set the media entity to the slot data we just assigned to the slot
$image->setMedia($media);
}
}
After that you should have more info in the slot variable in Twig to embed a media.

Stripe, is it possible to search a customer by their email?

Update: Since around January 2018, it is now possible to search using the email parameter on Stripe. See the accepted answer.
I was wondering if it was possible to search a customer only by their email address when using the Stripe API.
The documentation only indicates searching by:
created,
ending_before,
limit,
starting_after
But not email.
I'd like to avoid having to list over all my customers to find which ones have the same email addresses.
Stripe now allows you to filter customers by email.
https://stripe.com/docs/api#list_customers
Map<String, Object> options = new HashMap<>();
options.put("email", email);
List<Customer> customers = Customer.list(options).getData();
if (customers.size() > 0) {
Customer customer = customers.get(0);
...
This is important to help ensure you don't create duplicate customers. Because you can't put creating a customer in Stripe and the storage of the Stripe customer ID in your system inside a single transaction you need to build in some fail safes that check to see if a particular customer exists before you create a new one. Searching customers by email is important in that regard.
I did this by using the following API request. This was not available in stripe docs.I got this by tracking down their search in the dashboard area using Browser Developer Tools.
url :https://api.stripe.com/v1/search?query="+email+"&prefix=false",
method: GET
headers: {
"authorization": "Bearer Your_seceret Key",
"content-type": "application/x-www-form-urlencoded",
}
Warning This uses an undocumented API that is specific to the dashboard. While it might work today, there is no guarantee it will continue to work in the future.
You need to retrieve and store the Stripe customer ID along with the other customer details in your database. You can then search for the email address in your database and retrieve the customer record from Stripe by using the Stripe customer ID.
UPDATE: Stripe now allows searching via email
https://stripe.com/docs/api/php#list_customers
/**
* Remember that Stripe unfortunately allows multiple customers to have the same email address.
* #see https://stackoverflow.com/a/38492724/470749
*
* #param string $emailAddress
* #return array
*/
public function getCustomersByEmailAddress($emailAddress) {
try {
$matchingCustomers = [];
$lastResult = null;
$hasMoreResults = true;
while ($hasMoreResults) {
$searchResults = \Stripe\Customer::all([
"email" => $emailAddress,
"limit" => 100,
"starting_after" => $lastResult
]);
$hasMoreResults = $searchResults->has_more;
foreach ($searchResults->autoPagingIterator() as $customer) {
$matchingCustomers[] = $customer;
}
$lastResult = end($searchResults->data);
}
return $matchingCustomers;
} catch (\Exception $e) {
Log::error($e);
return [];
}
}
You can't directly search by email.
However, you can hack a little bit to list all users, and look after your email.
Here's my code (PHP) :
$last_customer = NULL;
$email = "EmailYou#AreLooking.for";
while (true) {
$customers = \Stripe\Customer::all(array("limit" => 100, "starting_after" => $last_customer));
foreach ($customers->autoPagingIterator() as $customer) {
if ($customer->email == $email) {
$customerIamLookingFor = $customer;
break 2;
}
}
if (!$customers->has_more) {
break;
}
$last_customer = end($customers->data);
}
You only need to write this line
\Stripe\Customer::all(["email" => "YourDesiredEmail"]);
In NodeJs we can search for our desired customer with their email address like the following:
const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
const stripe = require('stripe')(stripeSecretKey);
const findCustomerByEmail = async (email) => {
try {
const customer = await stripe.customers.list( {
email: email,
limit: 1
});
if(customer.data.length !== 0){
return customer.data[0].id;
}
} catch (e) {
return (e);
}
};
The actual call to stripe is using the stripe.customers.list. If the email exists in our stripe account then the returned object will contain an element called data.
Using "list()" and "search()", you can get customers by email with these Python code below:
customers = stripe.Customer.list(
email="example#gmail.com",
)
customer = stripe.Customer.search(
query="email:'example#gmail.com'"
)
You can also limit customers to get with "limit" parameter as shown below:
customers = stripe.Customer.list(
email="example#gmail.com",
limit=3
)
customer = stripe.Customer.search(
query="email:'example#gmail.com'",
limit=3
)
Since you specified that
The documentation only indicate to search by created, ending_before, limit and starting_after, but no "email".
You are right, you can't search using emails.
If you still wish to do that, What you can do instead is to get a list of all the customer and filter on the response you get using email.
For Example, in ruby you can do it as follows:
customers = Stripe::Customer.all
customer_i_need = customers.select do |c|
c.email == "foo#bar.com"
end
PS: Stripe can have multiple customers associated with one email address.
Please bear in mind when using Stripe API that it is case sensitive email (which is a bit stupid). Hopefully they change this.
Stripe API does not supports any search-by-email feature. They have this search in their dashboard but not released any to API; from the architectural concept it seems that there is no possibility or plan from stripe to include this in API; every object in their API is retrievable only by that specific objects id given by stripe while its created. May be, they have kept it as a scope for third party application developers involvement!!
So, the obvious solution is to store the customers in your own database that you want to be searchable in future - as Simeon Visser has said above
btw, for a workaround, if you already have used the stripe API a lot and there are many customer data which you now need to be searchable - the only way is to go thru the 'List all customers' functionality of API & build the database for your own purpose; ofcourse, you've to use pagination shortcut to iterate thru the whole list for doing so.
$customers = \Stripe\Customer::all(array("limit" => 3));
foreach ($customers->autoPagingIterator() as $customer) {
// Do something with $customer
}
You can try this. It worked for me. Below code will return empty list if not found data matching with email.
$stripe = new \Stripe\StripeClient("YOUR_STRIPE_SECRET");
$customers = $stripe->customers->all(['email'=>'jane.doe#example.com',
'limit' => 15,
]);
Stripe Search API Beta now is available
youtube link
Here is The Async- Await Way This Method can Be Used For All Third Party Hits with Nodejs Particularly
const configuration = {
headers: {
"authorization": `Bearer ${Your stripe test key}`,
"content-type": "application/x-www-form-urlencoded",
}
};
const requestUrl = `https://api.stripe.com/v1/search?
query=${'email you are to use'} &prefix=false`
const emailPayment = await axios.get(requestUrl,
configuration)
Axiom is Npm for creating http requests... very cool and dead simple
Stripe allows the ability to have more than once customer with the same email. That said, if you wanted to, you can pass a filters hash param to the list method to return a single customer or array of customers.
Stripe::Customer.list(email: user.email).data
The above will return an array of Stripe::Customer instances with the email

Laravel dynamic return from controller?

I need to return a different view from a controller depending on the route that is requested.
For example: in my application I have clients, devices and campaigns. All have CRUD's created, but in some cases I want to do something like "view clients, delete his campaign and returning to the clients view" but my campaignsController#delete returns to campaigns by default.
The idea is not rewrite the same controller only changing the route of returning, does Laravel have something to help with this?
Thank you
Laravel will not control the whole flow of your application. If you have a campaign delete router:
Route::delete('campaign/{id}');
and it returns to campaigns
class CampaignController extends Controller {
public function delete($id)
{
$c = Campaign::find($id);
$c->delete();
return Redirect::route('campaigns');
}
}
You will have to trick your route to make it go to wherever you need to, there should be dozens of ways of doing that, this is a very simple one:
Route::delete('campaign/{id}/{returnRoute?}');
class CampaignController extends Controller {
public function delete($id, $returnRoute = null)
{
$returnRoute = $returnRoute ?: 'campaigns';
$c = Campaign::find($id);
$c->delete();
return Redirect::route($returnRoute);
}
}
And create the links in those pages by using the return route option:
link_to_route('campaign.delete', 'Delete Campaign', ['id' => $id, 'returnRoute' => 'clients'])

my zend session name spacing does not work

I am new to Zend and very keen to learn, so I would really appreciate some help and guidance.
I am trying to create a 'method in a class' that will save the session variables of product pages visited by members to a site i.e
i,e examplesite com/product/?producttype= 6
I want to save the number 6 in a session variable. I also do not want to have a global session for the entire site; I just want it for selected pages. So, I guess I have to have Zend_Session::start() on the selected page; but I am not clear how this should be done.
Should I instantiate it in the page view page. i.e products page or do this in the indexAction() method for the products page. I have attempted to instantiate it below but it did not work.
public function rememberLastProductSearched()
{ //my attempt to start a session start for this particular page.
Zend_Session::start();
}
$session->productSearchCategory = $this->_request->getParam('product-search-category');
return" $session->productSearchCategory ";
}
else
{
//echo " nothing there
return " $session->productSearchCategory";
//";
}
}
With the rememberLastProductSearched() method I was trying to get the method to first check whether the user had searched for a new product or just arrived at the page by default. i.e whether he had used the get() action to search for a new product. If the answer is no, then I wanted the system to check whether their had been a previous saved session variable. so in procedural syntax it would have gone like this:
if(isset($_Get['producttype']))
{
//$dbc database connection
$producttype = mysqli_real_escape_string($dbc,trim($_GET['producttype']));
}
else
if(isset($_SESSION['producttype'])){
$producttype = mysqli_real_escape_string($dbc,trim($_SESSION['producttype']));
}
Can you please help me with the Zend/oop syntax. I am totally confused how it should be?
you're asking about simple work flow in an action, it should begin something like:
//in any controller
public function anyAction()
{
//open seesion, start will be called if needed
$session = new Zend_Session_Namespace('products');
//get value
$productCategory = $this->getRequest()->getParam('producttype');
//save value to namespace
$session->productType = $productCategory;
//...
}
now to move this off to a separate method you have to pass the data to the method...
protected function rememberLastProductSearched($productType)
{
//open seesion, start will be called if needed
$session = new Zend_Session_Namespace('products');
$session->productType = $productType;
}
So now if you want to test for presence of a value...
//in any controller
public function anyAction()
{
//open seesion, call the namespace whenever you need to access it
$session = new Zend_Session_Namespace('products');
if (!isset($session->productType)) {
$productCategory = $this->getRequest()->getParam('producttype');
//save value to session
$this->rememberLastProductSearched($productCategory)
} else {
$productCategory = $session->productType;
}
}
That's the idea.
Be mindful of your work flow as it can sometimes be very simple to inadvertently overwrite your session values.
$session = new Zend_Session_Namespace("productSearch");
if ($this->getRequest()->getParam('producttype')) { //isset GET param ?
$session->productType = $this->getRequest()->getParam('producttype');
$searchedProductType = $session->productType;
} else { //take the session saved value
if ($session->productType) {
$searchedProductType = $session->productType;
}
}
//now use $searchedProductType for your query

Update with multiple Models

I have two models that I would like to present in a single Yii Controller/View now I know how to make it so that it can Create from both however I resorted to a "hack" for the update side and I would appreciate some help making it right.
I have two models Recipes and RecipeSteps that in defined as a relation in the Recipe controller. RecipeSteps has a column "recipe_id" which is a foreign key and the Primary key of Recipes. Both tables are innoDB and have relations setup. RecipeSteps has ON DELETE CASCADE and ON UPDATE RETAIN set.
This is my actionUpdate from RecipesController.php
public function actionUpdate($id)
{
$model=$this->loadModel($id);
$recipeSteps=$this->loadModelSteps($id);
// Uncomment the following line if AJAX validation is needed
// $this->performAjaxValidation($model);
if(isset($_POST['Recipes']) && isset($_POST['RecipeSteps']))
{
$model->attributes=$_POST['Recipes'];
$recipeSteps->attributes=$_POST['RecipeSteps'];
if($model->save())
$recipeSteps->recipe_id = $model->recipe_id;
$recipeSteps->save();
$this->redirect(array('view','id'=>$model->recipe_id));
}
$this->render('update',array(
'model'=>$model,
'recipeSteps'=>$recipeSteps,
));
}
And this is my loadModel.
public function loadModel($id)
{
$model=Recipes::model()->findByPk($id);
if($model===null)
throw new CHttpException(404,'The requested page does not exist.');
return $model;
}
And here is the second one that I made for the other Model that I would like to condense into a single model.
public function loadModelSteps($id)
{
$recipeSteps=RecipeSteps::model()->findByPk($id);
if($recipeSteps===null)
throw new CHttpException(404,'The requested page does not exist.');
return $recipeSteps;
}
So what I would like to do is figure out how to only have one "LoadModel" that works for both models in actionUpdate.
try this on php 5.3.0 (Class Constants)
public function loadModel($id,$modelName){
$o=call_user_func(array($modelName,'model'));
$model = $o->findByPk($id);
if($modelByPk===null)
throw new CHttpException(404,'The requested page does not exist.');
return $modelByPk;
}