Sku not working in magento product update API - api

$result = $client->call($session, 'catalog_product.update', array('123', array(
'name' => 'Product333222'
)
)
);
Here '123' is the Sku of product. Sku is not working here in update Api.
If i give Product ID in place of Sku it is working fine.
So what is the Issue behind that.
If anyone Knows please let me know.
Thanks.

Magento is a bit dull here.
Long story short:
If you are using a numeric value without specifying an identification type its assuming you are doing your works on a product id. If you where to insert "abc" as a value (not numeric) it will be treated as if it were a SKU.
Best way to solve this is to use an identification type (in your case "SKU") in your api call.
Please see this for more info on using the identification type. http://www.magentocommerce.com/api/soap/catalog/catalogProduct/catalog_product.update.html
Or see: Magento 1.5, numeric SKUs and productIdentifierType
Short story long:
The following function gets called trough the api
app/code/core/Mage/Catalog/Model/Api/Resource.php
protected function _getProduct($productId, $store = null, $identifierType = null)
{
$product = Mage::helper('catalog/product')->getProduct($productId, $this->_getStoreId($store), $identifierType);
if (is_null($product->getId())) {
$this->_fault('product_not_exists');
}
return $product;
}
As you can see that function is calling the following function in the product helper:
public function getProduct($productId, $store, $identifierType = null) {
$loadByIdOnFalse = false;
if ($identifierType == null) {
if (is_string($productId) && !preg_match("/^[+-]?[1-9][0-9]*$|^0$/", $productId)) {
$identifierType = 'sku';
$loadByIdOnFalse = true;
} else {
$identifierType = 'id';
}
}
/** #var $product Mage_Catalog_Model_Product */
$product = Mage::getModel('catalog/product');
if ($store !== null) {
$product->setStoreId($store);
}
if ($identifierType == 'sku') {
$idBySku = $product->getIdBySku($productId);
if ($idBySku) {
$productId = $idBySku;
}
if ($loadByIdOnFalse) {
$identifierType = 'id';
}
}
if ($identifierType == 'id' && is_numeric($productId)) {
$productId = !is_float($productId) ? (int) $productId : 0;
$product->load($productId);
}
return $product;
}
Without specifying an $identifierType here and using a sku like '123' the thrid line is going to do a preg match with will result in true. Thus using its else function threating it as an ID in stead of sku.
In the end:
So, do your call like:
$result = $client->call($session, 'catalog_product.update', array('123', array(
'name' => 'Product333222'
), null, 'sku'));

Related

working with option of $query in yii2

i want use where for $query.
foreach ($oppId as $o) {
$id = $o['opportunity_id'];
$query->Where("id=$id");
}
When I use this. All items shown
$query->orWhere("id=$id");
i need get this query :
SELECT * FROM `opportunity` WHERE id =27 or id =28
this is all of my function :
public function actionShow($type = 0, $city = 0, $client = 0) {
$query = (new \yii\db\Query())->select(['*'])->from('opportunity ')->innerJoin('profile_details', 'opportunity.user_id=profile_details.user_id')->orderBy('id desc');
$query->Where('id !=-1');
if (isset($_REQUEST['type'])) {
$type = $_REQUEST['type'];
if ($type != 0) {
$query->andWhere("project_type_id=$type");
}
}
if (isset($_REQUEST['city'])) {
$city = $_REQUEST['city'];
if ($city != 0) {
$query->andWhere("state_id=$city");
}
}
if (isset($_REQUEST['client'])) {
$client = $_REQUEST['client'];
if ($client != 0) {
$oppId = \app\models\OpportunityControl::find()
->where('project_type_id = :project_type_id', [':project_type_id' => $client])
->all();
foreach ($oppId as $o) {
$id = $o['opportunity_id'];
$query->orWhere("id=$id");
}
}
}
You very much do not want to use strings to add to the query under any circumstances as that is ripe for SQL injection. I'd format it like this:
...
$params = [];
foreach ($oppId as $o) {
$params[] = $o->opportunity_id;
}
$query->andWhere(['in', 'id', $params]);
...
You should also adjust your other query params so that you are not passing variables into SQL via a string.
if (isset($_REQUEST['type'])) {
$type = $_REQUEST['type'];
if ($type != 0) {
$query->andWhere(['project_type_id' => $type]);
}
}
if (isset($_REQUEST['city'])) {
$city = $_REQUEST['city'];
if ($city != 0) {
$query->andWhere(['state_id' => $city]);
}
}
See the Yii2 guide on using variables in queries for what you are trying to avoid here. Specifically:
Do NOT embed variables directly in the condition like the following, especially if the variable values come from end user inputs, because this will make your application subject to SQL injection attacks.
// Dangerous! Do NOT do this unless you are very certain $status must be an integer.
$query->where("status=$status");
I do it with Arrays
$query->where(['or',['id'=>27],['id'=>28]]);
But in your case save all ids in a Array is not possible,I do it with string inside foreach
$StringWhere='';
$LastElement = end($oppId);
foreach ($oppId as $o)
{
$id = $o['opportunity_id'];
$StringWhere.=' id='.$id;
if($o!=$LastElement)
{
$StringWhere.=' or ';
}
}
$query->where($StringWhere);
$query->where(['or',['id'=>27],['id'=>28]]);
I use this and it works perfectly as mentioned by metola. :)

Yii check scenario in beforeSave()

For one action I need transform $album_id before save it to DB
in model function beforeSave() i do:
// преобразовать album -> album_id
$album_id=array();
foreach($this->string2array($this->album, '\|') as $one)
$album_id[]=Album::model()->findByAttributes(array('album' => $one))->id;
$this->album_id = $this->array2string($album_id);
but for another action I don't need this transform, because $album_id is already in proper state. So I set scenario 'batchcreate' in that action:
public function actionCreate()
{
Yii::import('ext.multimodelform.MultiModelForm');
$model = new Album('create');
$song = new Song();
$song->setScenario('batchcreate');
...
}
and try to check this scenario in model:
if(!($this->scenario === 'batchcreate')) {
// преобразовать album -> album_id
$album_id=array();
foreach($this->string2array($this->album, '\|') as $one)
$album_id[]=Album::model()->findByAttributes(array('album' => $one))->id;
$this->album_id = $this->array2string($album_id);
}
but the condition is always true. Why my scenario doesn't set or doesn't check in if statement?
Or maybe it's better to check not scenario, but make another variable, so how to set its value for 2 different cases?
my whole beforeSave():
protected function beforeSave()
{
if(parent::beforeSave())
{
// преобразовать whoes -> who
$who=array();
foreach($this->string2array($this->whoes) as $one) {
$userrow = User::model()->findByAttributes(array('username' => $one));
if($userrow) $who[]=CHtml::encode($userrow->id);
else $who[]=$one;
}
$this->who = $this->array2string($who);
//var_dump( $this->scenario );
if(!($this->scenario == 'batchcreate')) {
//if($this->notbatchcreate == 'yes') {
// преобразовать album -> album_id
$album_id=array();
foreach($this->string2array($this->album, '\|') as $one)
$album_id[]=Album::model()->findByAttributes(array('album' => $one))->id;
$this->album_id = $this->array2string($album_id);
}
return true;
}
else
return false;
}
Instead of
$song = new Song();
$song->setScenario('batchcreate');
you can simply do
$song = new Song('batchcreate');
In beforeSave()
if ( $this->scenario != 'batchcreate' ) {
echo "good - scenario is not batchcreate";
die();
}
echo 'nope...';
var_dump($this->scenario);
die();
Switch the order: call parent::beforeSave() after your code for checking the scenario. The inherited method beforeSave() may be altering your scenario.

Get raw sql from Phalcon query builder

Is it possible to extract raw sql query from the query builder instance in Phalcon? Something like this?
$queryBuilder = new Phalcon\Mvc\Model\Query\Builder();
$queryBuilder
->from(…)
->where(…);
$rawSql = $queryBuilder->hypotheticalGetRawQueryMethod();
By error and trial the below seems to working. Would be great if someone could confirm if there's a better way.
$queryBuilder = new Builder();
$queryBuilder->from(…)->where(…);
$intermediate = $queryBuilder->getQuery()->parse();
$dialect = DI::getDefault()->get('db')->getDialect();
$sql = $dialect->select($intermediate);
Edit: As of 2.0.3 you can do it super simple, see comment for full details:
$modelsManager->createBuilder()
->from('Some\Robots')
->getQuery()
->getSql()
you can use getRealSqlStatement() (or similar function name) on the DbAdapter. See http://docs.phalconphp.com/en/latest/api/Phalcon_Db_Adapter.html
According to documentation you can get this way the resulting sql query.
Or wait, this might not work on querybuilder. Otherwise you can setup low level query logging: http://docs.phalconphp.com/en/latest/reference/models.html#logging-low-level-sql-statements
$db = Phalcon\DI::getDefault()->getDb();
$sql = $db->getSQLStatement();
$vars = $db->getSQLVariables();
if ($vars) {
$keys = array();
$values = array();
foreach ($vars as $placeHolder=>$var) {
// fill array of placeholders
if (is_string($placeHolder)) {
$keys[] = '/:'.ltrim($placeHolder, ':').'/';
} else {
$keys[] = '/[?]/';
}
// fill array of values
// It makes sense to use RawValue only in INSERT and UPDATE queries and only as values
// in all other cases it will be inserted as a quoted string
if ((strpos($sql, 'INSERT') === 0 || strpos($sql, 'UPDATE') === 0) && $var instanceof \Phalcon\Db\RawValue) {
$var = $var->getValue();
} elseif (is_null($var)) {
$var = 'NULL';
} elseif (is_numeric($var)) {
$var = $var;
} else {
$var = '"'.$var.'"';
}
$values[] = $var;
}
$sql = preg_replace($keys, $values, $sql, 1);
}
More you can read there
The following is the common solution:
$result = $modelsManager->createBuilder()
->from(Foo::class)
->where('slug = :bar:', ['bar' => "some-slug"])
->getQuery()
->getSql();
But you might not expect to see the query without its values, like in:
die(print_r($result, true));
Array
(
[sql] => SELECT `foo`.`id`, `foo`.`slug` FROM `foo` WHERE `foo`.`slug` = :bar
[bind] => Array
(
[bar] => some-slug
)
[bindTypes] =>
)
So, this simple code might be useful:
public static function toSql(\Phalcon\Mvc\Model\Query\BuilderInterface $builder) : string
{
$data = $builder->getQuery()->getSql();
['sql' => $sql, 'bind' => $binds, 'bindTypes' => $bindTypes] = $data;
$finalSql = $sql;
foreach ($binds as $name => $value) {
$formattedValue = $value;
if (\is_object($value)) {
$formattedValue = (string)$value;
}
if (\is_string($formattedValue)) {
$formattedValue = sprintf("'%s'", $formattedValue);
}
$finalSql = str_replace(":$name", $formattedValue, $finalSql);
}
return $finalSql;
}
If you're using query builder then like given below then getPhql function can serve the purpose as per phalcon 3.4.4 version.
$queryBuilder = new Builder();
$queryBuilder->from(…)->where(…)->getQuery();
$queryBuilder->getPhql();
if (!function_exists("getParsedBuilderQuery")) {
/**
* #param \Phalcon\Mvc\Model\Query\BuilderInterface $builder
*
* #return null|string|string[]
*/
function getParsedBuilderQuery (\Phalcon\Mvc\Model\Query\BuilderInterface $builder) {
$dialect = Phalcon\Di::getDefault()->get('db')->getDialect();
$sql = $dialect->select($builder->getQuery()->parse());
foreach ($builder->getQuery()->getBindParams() as $key => $value) {
// For strings work fine. You can add other types below
$sql = preg_replace("/:?\s?($key)\s?:?/","'$value'",$sql);
}
return $sql;
}
}
Simple function that im using for debugging.

PHP PDO dynamically updating db table with multiple records to a specific user ID

/* Newbie need some help; I am creating a class to auto update my apps db record when instructed to, but I am consistently getting this message below, and for the heck of it, I just not seeing what I am doing wrong. Can someone please look at my codes for me? Thank you.
Warning: PDOStatement::bindParam() expects at least 2 parameters, 1 given in……..on line 331; that where the "else if(is_string($val)){" is located.
*/
// vars given
// DBDriver: MySQL
$myTable = 'seeYou';
$loginDate = NULL;
$ip = $_SERVER['REMOTE_ADDR'];
$date = #date('m/d/Y \a\\t h:i a');
$_id =1;
// data array
$idata = array("last_logged_in"=>$loginDate,
"login_date"=>$date,
"ip_addr"=>$ip
);
class name
{
///------------ other methods here---------///
/**
*--------------------------------------------
* Method - PDO: SET FIELD VALUE PLACEHOLDER
*--------------------------------------------
* #return fields with prefix as placeholder
*/
protected function set_fieldValPlaceHolders(array $data)
{
$set = '';
foreach($data as $field => $value)
{
$set .= $field .'= :'.$field . ',';
}
// remove the last comma
$set = substr($set, 0, -1);
return $set;
}
public function save($data=NULL, $_id = NULL, $rows= NULL, $dbTable= NULL)
{
//----------------- some other codes goes here ----------------//
$id = (int)$_id;
// update row with a specific id
if (isset($id) !== NULL && $rows === NULL)
{
$set = $this->set_fieldValPlaceHolders($data);
$sql = "UPDATE {$dbTable} SET {$set} WHERE user_id = :uid";
try
{
// Build the database statement
$_stmt = $this->_dbConn->prepare($sql);
$_stmt->bindValue(':uid',$id, PDO::PARAM_INT);
foreach ($data as $field => $val)
{
if(is_int($val)){
$_stmt->bindValue(':'.$field.'\', '.$val.', PDO::PARAM_INT');
}
else if(is_string($val)){
$_stmt->bindValue(':'.$field.'\', '.$val.', PDO::PARAM_STR');
}
else if(is_bool($val)){
$_stmt->bindValue(':'.$field.'\', '.$val.', PDO::PARAM_BOOL');
}
else if(is_null($val)){
$_stmt->bindValue(':'.$field.'\', '.$val="null".', PDO::PARAM_NULL');
}
else {
$_stmt->bindValue(':'.$field.'\', '.$val.', NULL');
}
$result = $_stmt->execute();
$num = $_stmt->rowCount();
}
}
catch(PDOException $e)
{
die('Error! The process failed while updating your record. <br /> Line #'.__LINE__ .' '.$e);
}
if ($result === true)
{
return true;
}
}
Check your bindValue calls: You give 1 parameter (a long string). It needs at least two. Check all the '
for example, it should be:
$_stmt->bindValue(':'.$field, $val, PDO::PARAM_INT);

How to get the options of a configurable attribute in Magento?

we want to export/import configurable products through the Magento-API in another system. What is important for us, are the values of the configurable products like a T-Shirt which has 3 colors (red, green and blue).
We receive the configurable attributes with the following function:
public function options($productId, $store = null, $identifierType = null)
{
$product = $this->_getProduct($productId, $store, $identifierType);
if (!$product->getId()) {
$this->_fault('not_exists');
}
$configurableAttributeCollection = $product->getTypeInstance()->getConfigurableAttributes();
$result = array();
foreach($configurableAttributeCollection as $attribute){
$result[$attribute->getProductAttribute()->getAttributeCode()] = $attribute->getProductAttribute()->getFrontend()->getLabel();
//Attr-Code: $attribute->getProductAttribute()->getAttributeCode()
//Attr-Label: $attribute->getProductAttribute()->getFrontend()->getLabel()
//Attr-Id: $attribute->getProductAttribute()->getId()
}
return $result;
}
But how is it possible to get the options used in that product (e.a. blue, green, red if the configurable attribute is "color") with the now available label/id from the configurable attribute which we got through the above function?
Answers are very appreciated!
Tim
Since we couldn't find a better solution, this is what I came up with:
public function options($productId, $store = null, $identifierType = null)
{
$_product = $this->_getProduct($productId, $store, $identifierType);
if (!$_product->getId()) {
$this->_fault('not_exists');
}
//Load all simple products
$products = array();
$allProducts = $_product->getTypeInstance(true)->getUsedProducts(null, $_product);
foreach ($allProducts as $product) {
if ($product->isSaleable()) {
$products[] = $product;
} else {
$products[] = $product;
}
}
//Load all used configurable attributes
$configurableAttributeCollection = $_product->getTypeInstance()->getConfigurableAttributes();
$result = array();
//Get combinations
foreach ($products as $product) {
$items = array();
foreach($configurableAttributeCollection as $attribute) {
$attrValue = $product->getResource()->getAttribute($attribute->getProductAttribute()->getAttributeCode())->getFrontend();
$attrCode = $attribute->getProductAttribute()->getAttributeCode();
$value = $attrValue->getValue($product);
$items[$attrCode] = $value[0];
}
$result[] = $items;
}
return $result;
}
Hope this helps anybody.
I'm not 100% sure that I understand the question ... assuming you want the values and labels for the configurable options for a specific product I assume this would work (tested on version 1.4.0.1)
public function options($productId, $store = null, $identifierType = null)
{
$product = $this->_getProduct($productId, $store, $identifierType);
if (!$product->getId()) {
$this->_fault('not_exists');
}
$configurableAttributeCollection = $product->getTypeInstance()->getConfigurableAttributes();
$result = array();
foreach($configurableAttributeCollection as $attribute){
$result[$attribute->getProductAttribute()->getAttributeCode()] = array(
$attribute->getProductAttribute()->getFrontend()->getLabel() => $attribute->getProductAttribute()->getSource()->getAllOptions()
);
//Attr-Code: $attribute->getProductAttribute()->getAttributeCode()
//Attr-Label: $attribute->getProductAttribute()->getFrontend()->getLabel()
//Attr-Id: $attribute->getProductAttribute()->getId()
}
return $result;
}
again not sure exactly what your looking for, but the $attribute->getProductAttribute()->getSource()->getAllOptions() function gave me the available options label and value.
Hope this helps. if not, let me where I misunderstood. Thanks!