So, I have built a very detailed collection in Laravel to give me very specific and clean data in a format that works within my frontend. Now, forgetting that this will be a LOT of data, I needed to add paginate. As this is just going to be infinite scroll, I wanted to use cursorPaginate
Unfortunately, the code below does not allow it. The standard error of Method Illuminate\Support\Collection::cursorPaginate does not exist
How would I be best to refactor this to give me the data required AND give me cursorPaginate? I am completely open to changing some of it, I just do not know where best to start.
Thanks in advance!
return Activity::get()->groupBy('batch_uuid')->map(function ($batch) {
return [
'description' => $batch->first()->description,
'uuid' => $batch->first()->batch_uuid,
'event' => $batch->first()->event,
'created_at' => $batch->first()->created_at,
'subject' => $batch->first()->subject_type::find($batch->first()->subject_id),
'activities' => $batch->map(function ($activity) {
return [
'item' => $activity->causer_type::find($activity->causer_id),
];
}),
];
})->sortByDesc('created_at')->values();
```
You should watch this video from Laravel Daily. First method mentioned in this video he uses LengthAwarePaginator but you can try CursorPaginator for your purpose.
Related
Given this on the model:
public $validate = [
'amount' => array(
'rule' => array('comparison', '>=', 0),
'message' => 'You must buy over 0 of this item!'
)
];
How can I validate param #2 of the below?
public function buy(int $item, int $amount) {
Validation seems to be built only for POST, which I'd like to opt out of here.
First things first, modifying the database with GET requests is an anti-pattern for many different reasons. Even if you assume a friendly user agent (which you never should!), browsers can behave quirky and do unexpected stuff like for example sending GET request multiple times (that is perfectly valid as GET is not ment to modify data), which they usually won't for POST/PUT/DELETE.
I would strongly suggest to change your endpoint to handle POST requests instead.
That being said, you can generally validate whatever you want, the validation mechanisms first and foremost just validate data, they don't know or care where it stems from. You can hand over whatever data you want to your model, and let it validate it:
$data = array(
'item' => $item,
'amount' => $amount,
);
$this->ModelName->set($data);
if ($this->ModelName->validates()) {
// data is valid
} else {
// data is invalid
$errors = $this->ModelName->validationErrors;
}
Moreover you can use CakePHP's validation methods completely manually too:
App::uses('Utility', 'Validation');
$isValid = Validation::comparison($amount, '>' 0);
This example of course doesn't make too much sense, given that $isValid = $amount > 0 would do the same, however it should just show that you can validate anything everywhere without models being involved.
See also
Cookbook > Models > Data Validation > Validating Data from the Controller
Cookbook > Models > Data Validation > Core Validation Rules
I got a little issue to solve. In my app I am handling with a lot of Models and each model does have something like:
ModelResource
ModelResourceCollection
ModelResourceOverview
ModelResourceOverviewCollection
The reason is: Sometimes I don't need all the information that are visible if I am using the ModelResource - in this case I am calling the ModelResourceOverview.
Example
PostResource
- title
- tags
- content
- author
Example
PostOverviewResource
- title
- author
As I have a lot of Models I have a huge number of ApiResource-Classes and I want to get rid of that.
I thought about using $this->when in the Resource and pass something like "full" or "overview" to the request in the Controller.
Example
$data
= new PostResource($this->post);
So my question is: Is it the best way to add this to the request or is there a handier/nicer way to handle this?
Laravel has a way to hide fields on the fly: Hiding attributes from json
e.g.
return $post->makeHidden('content')->toArray();
If your logic about "visible or not" is bound to the current request, then you should use when or mergeWhen as you mentioned (everything here https://laravel.com/docs/7.x/eloquent-resources#conditional-attributes ), therefore you'll only have 2 resources instead of 4
public function toArray($request)
{
return [
'title' => $this->title,
'author' => $this->author,
$this->mergeWhen($this->needsFullData($request), [
'tags' => $this->tags,
'content' => $this->content,
]),
];
}
protected function needsFullData($request)
{
//Your logic
}
I am using bouncer for my ACL needs and ever since upgrading my project from laravel 5.7 to 5.8 I've noticed a significant increase in the time it takes for my requests to process.
I'm dealing with two models (let's call them Parent and Child), as well as the permissions the authenticated user has over them.
// Takes about 110ms. Eager loads various nested relationships and counters with specific constraints
$parents = Parent::myScope(...)->get();
// Bottleneck. Takes 5 minutes (!). Used to take about 40 seconds on laravel 5.7
$parents->each(function ($parent) {
$parent->permissions = [
'edit' => auth()->user()->can('edit', $parent),
'delete' => auth()->user()->can('delete', $parent),
'restore' => auth()->user()->can('restore', $parent)
];
$parent->children()->each(function ($child) {
$child->permissions = [
'edit' => auth()->user()->can('edit', $child),
'delete' => auth()->user()->can('delete', $child),
'restore' => auth()->user()->can('restore', $child)
];
}
}
I'm appending the permissions like this because the $parents variable will be sent as json to the front-end. I'm pretty sure this implementation is wrong, and must have a better alternative but the real issue is this inexplicable five-fold increase in loading time.
The times were obtained using Debugbar measures.
Using the monitor command in redis-cli (I'm using Redis to cache the permissions), I've noticed the GET requests come more slowly than before. In fact, even after I stop a page from loading (ESC), the GET requests to Redis don't stop immediately. I'm not sure if this is normal behavior or not.
I tried to check the issues at the bouncer repo but I haven't found anything.
You're calling auth()->user() hundreds of times. Can you try calling it only once?
$user = auth()->user();
$parents->each(function ($parent) use ($user) {
$parent->permissions = [
'edit' => $user->can('edit', $parent),
'delete' => $user->can('delete', $parent),
'restore' => $user->can('restore', $parent)
];
$parent->children()->each(function ($child) {
$child->permissions = [
'edit' => $user->can('edit', $child),
'delete' => $user->can('delete', $child),
'restore' => $user->can('restore', $child)
];
}
}
Also, since you're eager-loading the children, you shouldn't fetch them all again within each loop iteration:
$parent->children()->each(function ($child) {
// ^^ remove these parentheses
$child->permissions = [
'edit' => $user->can('edit', $child),
'delete' => $user->can('delete', $child),
'restore' => $user->can('restore', $child)
];
}
After some testing a solution was found. It turns out, there was no problem with the code at all.
Something is wrong with the server. We do not know exactly what but trying to run the project on freshly installed machines got rid of those awful processing times. (Now times are at 15s on first request)
The server's problem got worse coincidentally after migrating from laravel 5.7 to 5.8 which led me to this wild goose chase.
ADDENDUM
The culprit was Xdebug. We used it to get code-coverage analysis but the performance was so bad we ended up switching to phpdbg.
How can I create a model rule that's only required when a certain value from the Database is 1?
I tried using a 'required', 'when' rule but that doesn't seem to update the client-side JavaScript.
I also tried a custom inline validator but that doesn't seem to post an empty field.
Scenario's aren't an option I think as I have 6 fields and can have any combination of required/not required.
EDIT
At the moment I just never add the required rules, instead of directly returning the rules I store them in a variable. $rules = []
Then before I return the variable I add the required options to the array.
if($x->x_required)
$rules[] = ['your-field', 'required', 'on' => 'your-scenario'];
This is a quickfix and I don't really like it, but it works. I'm not sure if there is a better way of doing this.
You need to use combination required with when, but for client side validation you need additionally specify whenClient property.
Example (add this to your rules()):
[
'attributeName',
'required',
'when' => function ($model) {
return $model->country == Country::USA;
},
'whenClient' => function (attribute, value) {
return $('#country').value == 'USA';
},
],
Official docs:
RequiredValidator
Validator $when
Validator $whenClient
I am currently working on a project that involves scraping data from over 300 static pages and transferring that data into a WordPress site. I have set up various custom fields with the Simple Fields (http://simple-fields.com/) plugin, and have a basic XML-RPC connection working. Below is what I am using to test injecting custom posts (written in Ruby):
connection = XMLRPC::Client.new_from_uri "http://localhost:8888/xmlrpc.php"
username = "admin"
password = "password"
test_post = {
:post_type => "custom_property",
:post_status => "draft",
:post_title => "test post!",
:post_meta => {
:_simple_fields_fieldGroupID_1_fieldID_3_numInSet_0 => "test"
}
}
puts connection.call("wp.newPost", 1, username, password, test_post)
I have tried :custom_fields in place of :post_meta but still to no avail. I can't seem to be able to update the custom fields that have been created through Simple Fields.
If anyone has any suggestions on what to do, it would be greatly appreciated.
EDIT: This problem was solved by extending the XML-RPC functions using this article: http://kovshenin.com/2010/custom-xml-rpc-methods-in-wordpress/
It is actually quite tricky to figure out the structure of the Wordpress post. If it is a custom field you want to post in, you should use the format below:
'custom_fields' => [{'key' => 'something', 'value' => 'something else'}]
If it is custom taxonomy, use this:
'terms' => {
'custom_type' => [value],
'custom_something' => ['array_item','array_item']
}
For terms you always need to put the value into an array even if it's just one item. Hope it helped.