Yii check scenario in beforeSave() - yii

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.

Related

Yii2 Update the entry if it exists

Item $title defined earlier. I want what if found $find_title, update it with all fields from $title. Otherwise, create a new object
$find_title = Title::find()->where(["upc" => $title->upc])->one();
if ($find_title != null) {
$title->id = $find_title->id;
$title->save();
} else {
$title->save();
}
It worked in the laravel.
$find_title = Title::find()->where(["upc" => $title->upc])->one();
if ($find_title != null) {
$id = $title->id;
$find_title->attributes = $title->attributes;
$find_title->id = $id;
$find_title->save();
} else {
$title->save();
}
Here you assign all attributes of $title to $find_title, restore id, and then save;

Moq + xunit + asp.net core: service return null value

I use xUnit as test runner in my asp.net core application.
Here is my test theory:
[Theory(DisplayName = "Search advisors by advisorId"),
ClassData(typeof(SearchAdvisorsByIdTestData))]
public async void SearchAdvisors_ByAdvisorId(int brokerDealerId, FilterParams filter)
{
// Arrange
var _repositoryMock = new Mock<IRepository>();
// Do this section means we bypass the repository layer
_repositoryMock
.Setup(x => x.SearchAdvisors(filter.CID.Value, new AdvisorSearchOptions
{
SearchKey = filter.SeachKey,
AdvisorId = filter.AdvisorId,
BranchId = filter.BranchId,
City = filter.City,
Skip = filter.Skip,
Limit = filter.Limit,
RadiusInMiles = filter.Limit,
Longitude = filter.Longitude,
Latitude = filter.Latitude
}))
.Returns(Task.FromResult<SearchResults<Advisor>>(
new SearchResults<Advisor>()
{
Count = 1,
Limit = 0,
Skip = 0,
ResultItems = new List<SearchResultItem<Advisor>>() {
//some initialize here
}
})
);
_advisorService = new AdvisorService(_repositoryMock.Object, _brokerDealerRepositoryMock, _brokerDealerServiceMock);
// Action
var model = await _advisorService.Search(brokerDealerId, filter);
Assert.True(model.AdvisorResults.Count == 1);
Assert.True(model.AdvisorResults[0].LocationResults.Count > 0);
}
The service like this
public async Task<ViewModelBase> Search(int brokerDealerId, FilterParams filter)
{
var opts = new AdvisorSearchOptions
{
SearchKey = filter.SeachKey,
AdvisorId = filter.AdvisorId,
BranchId = filter.BranchId,
City = filter.City,
Skip = filter.Skip,
Limit = filter.Limit,
RadiusInMiles = filter.Limit,
Longitude = filter.Longitude,
Latitude = filter.Latitude
};
var searchResults = await _repository.SearchAdvisors(filter.CID.Value, opts); // line 64 here
if (searchResults.Count == 0 && Utils.IsZipCode(filter.SeachKey))
{
}
//Some other code here
return model;
}
The issue was after run line 64 in the service. I always get null value of searchResults although I already mocked _repository in the test.
What was my wrong there?
Thank in advance.
Argument matcher for SearchAdvisors() mock does not work because you pass different instances of AdvisorSearchOptions. First instance is created in _repositoryMock.Setup() statement and the second one is created in Search() method itself.
There are several ways to fix this problem:
1.If you don't care about verifying whether instance of AdvisorSearchOptions passed to repository is filled correctly, just use It.IsAny<AdvisorSearchOptions>() matcher in mock setup:
_repositoryMock.Setup(x => x.SearchAdvisors(filter.CID.Value, It.IsAny<AdvisorSearchOptions>()))
.Returns(/*...*/);
2.In previous case the test will not verify that AdvisorSearchOptions is filled correctly. To do this, you could override Object.Equals() method in AdvisorSearchOptions class so that mock call will match for different instances:
public class AdvisorSearchOptions
{
// ...
public override bool Equals(object obj)
{
var cmp = obj as AdvisorSearchOptions;
if (cmp == null)
{
return false;
}
return SearchKey == cmp.SearchKey && AdvisorId == cmp.AdvisorId &&
/* ... compare all other fields here */
}
}
3.Another way to verify object passed to mock is to save the instance via Mock callback and then compare required fields:
AdvisorSearchOptions passedSearchOptions = null;
_repositoryMock
.Setup(x => x.SearchAdvisors(filter.CID.Value, It.IsAny<AdvisorSearchOptions>()))
.Returns(Task.FromResult<SearchResults<Advisor>>(
new SearchResults<Advisor>()
{
Count = 1,
Limit = 0,
Skip = 0,
ResultItems = new List<SearchResultItem<Advisor>>() {
//some initialize here
}
})
)
.Callback<int, AdvisorSearchOptions>((id, opt) => passedSearchOptions = opt);
// Action
// ...
Assert.IsNotNull(passedSearchOptions);
Assert.AreEqual(filter.SearchKey, passedSearchOptions.SearchKey);
Assert.AreEqual(filter.AdvisorId, passedSearchOptions.AdvisorId);
// Check all other fields here
// ...

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. :)

Sku not working in magento product update 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'));

optimize a SQL query for a couchdb view

How to optimize this SQL query for a couchdb view ?
SELECT * FROM db WHERE user = '$userid' OR userFollowed = '$userid'
The couchdb database contains this structure of documents:
_id
user
userFollowed
This because a user can follows another and viceversa and my scope is to get all followers of user A that this user which follows it turn, for example:
A follows B
B follows A
In this example I need to get B, enstabilishing that both users are followers... I know it's complex to explain and understand but I'll try with the things I'm doing with node.js and cradle.
The view map:
function (doc) {
emit(doc.user, doc.userFollowed)
}
The node.js code:
db.view("followers/getFollowers", function(err, resp) {
if (!err) {
var followers = [];
resp.forEach(function(key, value, id) {
var bothFollow = false;
if (key == userID) {
if (followers.indexOf(value) == -1) {
resp.forEach(function(key, value, id) {
if (value == userID)
bothFollow = true;
});
if (bothFollow)
followers.push(value);
}
} else if (value == userID) {
if (followers.indexOf(key) == -1) {
resp.forEach(function(key, value, id) {
if (key == userID)
bothFollow = true;
});
if (bothFollow)
followers.push(key);
}
}
});
console.log(followers);
}
});
So in the code first I check if the A or B values corrispondes to the other user, then check with another loop if there is a relationship putting the follower in the list
All this code works but I don't think that's the correct procedure and maybe I'm wrong anything :( can you help me please ?
It is easier to emit both users in the view function:
function (doc) {
emit(doc.user, null);
emit(doc.userFollowed, null);
}
Than you can just call the view and will get a plain list:
http://localhost:5984/db/_design/app/_view/followers?include_docs=true