I am trying to set the limit on my active data provider in Yii2 in the pagination to 5 records. Accordingly to THIS documentation, the $limit property is available, but I am getting this error:
Setting read-only property: yii\data\Pagination::limit
And my code is this:
$dataProvider = new ActiveDataProvider([
'query' => Order::find()
->where(['user_id' => $user_id]),
'sort' =>[
'defaultOrder' => [
'id' => SORT_DESC
]
],
'pagination' => [
'pageSize' => 20,
'limit' => 5,
],
]);
And my question is this: Why does this happen? The pageSize works if I erase the limit everything goes smooth... On limit everything fails... How can I solve this?
L.E: I found the source method that throws this error, in case it helps:
/**
* Sets value of an object property.
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when executing `$object->property = $value;`.
* #param string $name the property name or the event name
* #param mixed $value the property value
* #throws UnknownPropertyException if the property is not defined
* #throws InvalidCallException if the property is read-only
* #see __get()
*/
public function __set($name, $value)
{
$setter = 'set' . $name;
if (method_exists($this, $setter)) {
$this->$setter($value);
} elseif (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
} else {
throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
}
}
according to this, it seams I am trying to: getLimit() not setLimit()... any ideas why?
Pagination doesn't have property $limit,
#property integer $limit The limit of the data. This may be used to set the LIMIT value for a SQL statement
That value set by propery $pageSize.
If you need to get last 5 news and this must be work with pager you need something like this:
$dataProvider = new ActiveDataProvider([
'query' => Order::find()
->where(['user_id' => $user_id]),
'sort' =>[
'defaultOrder' => [
'id' => SORT_DESC
]
],
'pagination' => [
'pageSize' => 5,
],
]);
Many properties in Yii are read-only. There are 2 simple ways to find out whether a property is read only or not.
Using canSetProperty()
if there is a method with set prefix, for example setLimit()
On the other hand, you must set limit on your query, not pagination.
Maybe ArrayDataProvider will be better suited.
$query = new Query;
$provider = new ArrayDataProvider([
'allModels' => $query->from('post')->limit(5)->all(),
'key' => 'id',
'sort' => [
'defaultOrder' => [
'id' => SORT_DESC
]
]
]);
According to the Yii Guid Concept of Properties
A property defined by a getter without a setter is read only. Trying to assign a value to such property will cause an InvalidCallException. Similarly, a property defined by a setter without a getter is write only, and trying to read such property will also cause an exception. It is not common to have write-only properties.
There are several special rules for, and limitations on, the properties defined via getters and setters:
The names of such properties are case-insensitive. For example, $object->label and $object->Label are the same. This is because method names in PHP are case-insensitive.
If the name of such property is the same as a class member variable, the latter will take precedence. For example, if the above Foo class has a member variable label, then the assignment $object->label = 'abc' will affect the member variable 'label'; that line would not call the setLabel() setter method.
These properties do not support visibility. It makes no difference for the visibility of a property if the defining getter or setter method is public, protected or private.
The properties can only be defined by non-static getters and/or setters. Static methods will not be treated in the same manner.
So in other terms, there is no way do do this only with PHP. As it is said in the documentation, a thing that I just saw:
$limit public read-only property
integer getLimit( )
The limit of the data. This may be used to set the LIMIT value for a SQL statement for fetching the current page of data. Note that if the page size is infinite, a value -1 will be returned.
In order for this to work as expected, the code needs to become this:
$dataProvider = new ActiveDataProvider([
'query' => Order::find()
->where(['user_id' => $user_id])
->limit(5),
'sort' =>[
'defaultOrder' => [
'id' => SORT_DESC
]
],
'pagination' => false,
]);
Hope this helps others in my shoes!
Related
I am trying to add an asset link to a nested properties value using Eloquents API resource function:
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'image' => isset($this->image) ? asset('storage/'.$this->image) : null,
'properties' => $this->properties,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at
];
}
The following works fine for the image value, but I am using a nested properties['pdf'] file that I need to add asset('storage/') parameter to so it outputs the full URL.
How am I able to pass isset($this->properties['pdf']) ? asset('storage/'.$this->properties['pdf']) : null into the properties value? I still need the pdf value to return inside the properties value.
Note: There are other values inside properties but they are dynamic based on the data returned.
Probably not the cleanest idea but this worked:
$properties = $this->properties;
if(isset($this->properties['pdf']) && $this->properties['pdf'] != null){
$properties['pdf'] = asset('storage/'.$this->properties['pdf']);
}
Then I applied $properties to the return.
I'm pretty new to Phpspec testing and I don't know what is the correct way to test multiple scenarios when transforming a object to different response structure.
I need to check if price is correctly calculated. Here I have the Transformer spec test:
/**
* #dataProvider pricesProvider
*/
public function it_should_check_whether_the_prices_are_correct(
$priceWithoutVat,
$priceWithVat,
$vat,
Request $request,
Repository $repository
) {
$productIds = array(100001);
$result = array(
new Product(
'100001',
'MONSTER',
new Price(
$priceWithoutVat,
20,
'GBP',
null,
null
)
)
);
$expected = array(
array(
"productId" => "100001",
"brand" => "MONSTER",
"price" => array(
"amount" => $priceWithVat,
"vatAmount" => $vat,
"currencyCode" => "GBP",
"discountAmount" => (int)0
)
)
);
$repository->getResult(array(
Repository::FILTER_IDS => $productIds
))->willReturn($result);
$request->get('productIds')->willReturn(productIds);
/** #var SubjectSpec $transformedData */
$transformedData = $this->transform($request);
$transformedData->shouldEqual($expected);
}
public function pricesProvider()
{
return array(
array('123.456789', 14814, 2469),
array('60.00', 7200, 1200),
);
}
In my Transformer class I have a function which formats data to the correct format:
public function transform(Request $request)
{
$productIds = $request->get('productIds');
$productsResult = $this->repository->getResult(array(
Repository::FILTER_IDS => $productIds
));
$products = array();
foreach ($productsResult as $product) {
$products[] = $this->formatData($product);
}
return $products;
}
/**
* #param Product $product
* #return array
*/
private function formatData(Product $product)
{
return array(
'productId' => $product->getId(),
'brand' => $product->getBrandName(),
'price' => array(
'amount' => (int)bcmul($product->getPrice()->getAmountWithTax(), '100'),
'vatAmount' => (int)bcmul($product->getPrice()->getTaxAmount(), '100'),
'currencyCode' => $product->getPrice()->getCurrencyCode(),
'discountAmount' => (int)bcmul($product->getPrice()->getDiscountAmount(), '100')
)
);
}
The problem is, that I'm getting this error message:
316 - it should check whether the prices are correct
warning: bcmul() expects parameter 1 to be string, object given in
/src/AppBundle/Database/Entity/Product/Price/Price.php line 49
If I hard-code those values then the test is green. However I want to test varios prices and results, so I decided to use the dataProvider method.
But when dataProvider passes the $amountWithoutTax value, it's not string but PhpSpec\Wrapper\Collaborator class and because of this the bcmul fails.
If I change the $amountWithoutTax value to $priceWithoutVat->getWrappedObject() then Double\stdClass\P97 class is passed and because of this the bcmul fails.
How do I make this work? Is it some banality or did I completely misunderstood the concept of this?
I use https://github.com/coduo/phpspec-data-provider-extension and in composer.json have the following:
"require-dev": {
"phpspec/phpspec": "2.5.8",
"coduo/phpspec-data-provider-extension": "^1.0"
}
If getAmountWithTax() in your formatData method returns an instance of PhpSpec\Wrapper\Collaborator, it means that it returns a Prophecy mock builder instead of the actual mock, i.e. the one that you get by calling reveal() method. I don't know how your data provider looks like, but it seems that you're mocking your Price value objects instead of creating real instances thereof, and $product->getPrice() in your production code returns the wrong kind of object.
The solution would be either to create a real instance of the Price value object that's later returned by $product->getPrice() with new in the data provider, or by calling reveal() on that instance, like this (assuming $price is a mock object that comes from a type hinted parameter):
$product->getPrice()->willReturn($price->reveal());
I'm adding a virtual property within a Symfony entity class. This property shall be computed based on another table data - specifically on a column that is of the Doctrine array type.
class RelatedEntity
{
/* ... */
/**
* #ORM\Column(type="array")
*/
protected $type;
The point is I would like to use Doctrine Criteria for this as it's supposed to be optimized on SQL level. So I did this:
public function getCreated()
{
$criteria = Criteria::create()->where(Criteria::expr()->contains('type', 'create'));
$relatedEntity = $this->getRelatedEntities()->matching($criteria);
if (!$relatedEntity) {
return null;
}
return $relatedEntity->getTimestamp();
}
But I get an empty result set. Even though Doctrine is building a correct SQL statement, which works when I type it manually into the PostgreSQL database.
...WHERE type LIKE '%create%'
What is wrong with this approach and how can it be solved? Right now I did the trick with the ArrayCollection filter method, but it loads all related entities I don't need.
Thank you for any ideas.
EDIT: This is not a duplicate of the mentioned question as I cannot use EntityManager or EntityRepository inside an entity. I need to use Criteria, so the solution proposed in the question doesn't work for me.
Check the results of getRelatedEntities()
Depending on how this collection was created, any one of several things may be happening. In particular, it may be using entity aliases, or may not be returning any which match your Criteria.
Collection populated from an aliased entity (i.e.: via a QueryBuilder join/select).
If getRelatedEntities is populated by Doctrine via QueryBuilder, you've likely aliased the Entities.
EX.: $queryBuilder->addSelect('thing')->leftJoin('root_alias.entity',
'thing')
In such a case, the Criteria must use the alias:
Criteria::expr()->contains('thing.type', 'create')
No matches for Criteria.
Dump your collection before filtering it, this could be a simple case of your query having already filtered out any potential matches.
Test your Criteria
All things considered, without any clue as to the structure of the collection you're trying to filter, we can only assess your criteria. Thus, test your criteria, and check the contents of the collection you are attempting to filter.
$criteria = Criteria::create()->where(Criteria::expr()->contains('type', 'create'));
$collection = new ArrayCollection([
[
'key' => 1,
'type' => 'somethingcreatesomething',
],
[
'key' => 2,
'type' => 'abra',
],
[
'key' => 3,
'type' => 'cadabra',
],
[
'key' => 4,
'type' => 'alacreate',
],
]);
dump($collection->matching($criteria));
Result
Doctrine\Common\Collections\ArrayCollection {#2536
-elements: array:2 [
0 => array:2 [
"key" => 1
"type" => "somethingcreatesomething"
]
3 => array:2 [
"key" => 4
"type" => "alacreate"
]
]
}
I'm using laravel and elasticsearch-php to index and store data to elastic, my problem is that elastisearch uses from dynamic mapping but I need to set my custom mapping. How can I use from my mapping?
Bellow is my code:
$client = \Elasticsearch\ClientBuilder::create()->build();
$mappingData = array(
'index' => 'promote_kmp',
'body' => array(
'mappings' => $resource->getMappingProperties()
)
);
$client->indices()->create($mappingData);
$params = [
'type' => 'resources',
'id' => uniqid(),
'body' => [
'id' => $resource->id,
'name' => $resource->name,
'display_name_en' => $resource->display_name_en,
'display_name_pr' => $resource->display_name_pr,
'display_name_pa' => $resource->display_name_pa,
'table_name' => $resource->table_name,
'model_name' => $resource->model_name,
'in_sidemenu' => $resource->in_sidemenu,
'icon_class' => $resource->icon_class,
'created_at' => $resource->created_at,
'created_by' => $user,
]
];
//$response = $client->indices()->create($resource->getMappingProperties());
$client->index($params);
$resource->getMappingProperties() get the mapping array I have set in model.
but when I want to index a record it says IndexAlreadyExistsException[[promote_kmp] already exists]. This question arise when I want to search for date field searching is not properly working and I guess that mapping is not true.
As I was saying in comments.
The code is executing the creation of index every time you want to query.
But the index must be created only once.
So it should work like the migration for the DB's.
The only idea I can give you is to make a command to generate the index.
So that you could just
$ artisan elasticsearch:generate <index>
About the code, what I've done for our case, made the index with a way to inject the types, plus a way to create them into elasticsearch:
interface Index {
/**
* #param Type[] $types Index types (resources)
*/
function setTypes(array $types);
/**
* Generate the index and the types into the elasticsearch
*/
function create();
}
Then the types should generate the mappings and the type name (as /<index>/<type>, like:
interface Type {
/**
* #return string The type name
*/
function getName();
/**
* #return array The type mapping
*/
function getMapping();
}
So (somewhere), you would create the class (this could be better):
$myIndex = new MyIndex();
$myIndex->setTypes([
new MyFirstType(),
new MySecondType(),
//...
]);
$myIndex->create();
I hope this helps.
I've got error
Object of class Closure could not be converted to string
on this code
'class' => \dosamigos\grid\EditableColumn::className(),
'attribute' => 'remidi3',
'url' => function($data){return ['update?id=remidi3&dataid'.$data->id];},
'type' => 'text',
'editableOptions' => [
'mode' => 'inline',
]
even I've try to change
'url' => function($data){return ['update?id=remidi3&dataid'.$data->id];}
into
'url' => function($data){return 'update?id=remidi3&dataid'.$data->id;},
I need to display id in the URL of editable grid, somebody can help me?
According to source code and PHPDoc, you can't specify closure here.
PHPDoc says:
/**
* #var string the url to post
*/
public $url;
Usage in source code:
if ($this->url === null) {
throw new InvalidConfigException("'Url' property must be specified.");
}
...
$url = (array) $this->url;
$this->options['data-url'] = Url::to($url);
As you can see, it's converted to array and then processed by Url::to(), so the valid types are string and array.
I don't think you need to specify id in url, it should be taken automatically depending on row you working with.