Member Since 10 Months Ago
2,880 experience to go until the next level!
In case you were wondering, you earn Laracasts experience when you:
Earned once you have completed your first Laracasts lesson.
Earned once you have earned your first 1000 experience points.
Earned when you have been with Laracasts for 1 year.
Earned when you have been with Laracasts for 2 years.
Earned when you have been with Laracasts for 3 years.
Earned when you have been with Laracasts for 4 years.
Earned when you have been with Laracasts for 5 years.
Earned when at least one Laracasts series has been fully completed.
Earned after your first post on the Laracasts forum.
Earned once 100 Laracasts lessons have been completed.
Earned once you receive your first "Best Reply" award on the Laracasts forum.
Earned if you are a paying Laracasts subscriber.
Earned if you have a lifetime subscription to Laracasts.
Earned if you share a link to Laracasts on social media. Please email [email protected] with your username and post URL to be awarded this badge.
Earned once you have achieved 500 forum replies.
Earned once your experience points passes 100,000.
Earned once your experience points hits 10,000.
Earned once 1000 Laracasts lessons have been completed.
Earned once your "Best Reply" award count is 100 or more.
Earned once your experience points passes 1 million.
Earned once your experience points ranks in the top 50 of all Laracasts users.
Earned once your experience points ranks in the top 10 of all Laracasts users.
Replied to Query Using Recursive CTE
i am using MySQL 8.
I ended up using sql functions because for some reason the categories.id
was not synchronised and its value was always the same when i used the query above
Started a new Conversation Query Using Recursive CTE
I have the following tables
Category
- id
- parent_id
- title
Threads
- id
- category_id
- title
A category have sub-categories and only the "leaf" category can have threads To explain further with an example
Computer -> Mac -> macbook
In this case the category macbook
can have threads while the parent categories can't have threads.
What i want to achieve is to find the latest thread of a category, among all sub-categories
Computer -> Mac -> macbook
Computer -> Windows-> HP
In the example above, the categories macbook
and HP
have threads and i want for the parent category Computer
to find the latest thread among the categories macbook
and HP
To achieve that i am using recursive CTE
and it seems to work when i test it with sqlite
but when i use mysql
it doesn't work
public function scopeWithLatestThread($query)
{
return $query->addSelect(DB::raw('(
SELECT
id
FROM
threads
WHERE
threads.category_id in
(
with recursive recursive_categories (id) as
(
SELECT id
from categories as initial_categories
where initial_categories.id=categories.id
UNION ALL
SELECT remaining_categories.id
FROM recursive_categories JOIN categories as remaining_categories
on recursive_categories.id=remaining_categories.parent_id
)
SELECT id FROM recursive_categories
)
ORDER BY
updated_at DESC
LIMIT 1) AS latest_thread_id')
)->with('latestThread');
}
public function latestThread()
{
return $this->belongsTo(Thread::class);
}
Category::whereNull('parent_id')->withLatestThread()->get();
Replied to Config Db Connection During Testing
no i didn't because i need to change the database connection in a small number of tests within various test files.
changing the settings in phpunit.xml
per test seems the same to me which is not very convenient.
i'd rather not setting env variables manually
Started a new Conversation Config Db Connection During Testing
In the phpunit.xml
i have the following settings
<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>
However, in some cases i need to use mysql
and i set the config in the tests that i need mysql like
config(['database.default' => 'mysql']);
config(['database.connections.mysql.database' => 'dbname')]);
This works for Feature
tests but for some reason when i do the same thing for Unit
tests it does not work and i can't figure out why.
I have tried to do config:clear
and cache:clear
but it didn't help
Any ideas ?
Started a new Conversation Policy For Polymorphic Model
I have a model named Reply
which can be either a
In each case the policy for deleting a reply is different
Right now i have a ReplyPolicy
class ReplyPolicy
{
public function deleteReply(User $user, Reply $reply)
{
//
}
public function deleteComment(User $user, Reply $reply)
{
//
}
public function deleteMessage(User $user, Reply $reply)
{
//
}
}
And when i need to authorize the user i do
$this->authorize('deleteReply', $reply);
$this->authorize('deleteComment', $comment);
$this->authorize('deleteMessage', $message);
Is there a better approach to handle this case ?
Started a new Conversation Set Env Variable In Phpunit At Runtime
by default the database variables in the phpunit.xml
are the following:
<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>
In one of my tests, i want to set the DB_CONNECTION
to mysql
because i test a functionality which uses whereJsonContains
which is not supported by sqlite
.
Is there a way to change the DB_CONNECTION
at runtime ?
putenv
and config
but none of them workmysql
because it is considerably slower than using memory
databaseStarted a new Conversation Difference Between WhereColumn And Where Clause
i have the following table, model and scope
conversations
- id
reads
- id
- readable_id
- readable_type
- user_id
- read_at
users
- id
I have the following scope for the Conversation model
public function scopeWithRead($query)
{
return $query->addSelect(['read_at' => Read::select('reads.read_at')
->whereColumn('reads.readable_id', 'conversations.id')
->whereColumn('reads.readable_type', 'App\Conversation')
->whereColumn('reads.user_id', auth()->id()),
]);
}
when i test this scope i get the correct results but when i actually try to use it from the front end i get 500
error
$conversations = Conversation::withRead()->get();
On the other hand, if i change from whereColumn to where, i get wrong results when i test it and the front end does not give any errors, however the results are wrong
public function scopeWithRead($query)
{
return $query->addSelect(['read_at' => Read::select('reads.read_at')
->where('reads.readable_id', '=', 'conversations.id')
->where('reads.readable_type', '=', 'App\Conversation')
->where('reads.user_id', '=', auth()->id()),
]);
}
Any thoughts on what is wrong ?
Replied to Determine If Model Is Recordable
@wingly thanks for the suggestion
i thought of that but it doesn't depend only on the repliable
model. There is a case where it depends on another column of the replies
table.
Started a new Conversation Determine If Model Is Recordable
I have a trait that records the activity of a model
RecordsActivityTrait
One of the models is polymorphic and in some cases i don't want to record the activity for that particular case.
The model name is Reply
and it has a repliable
relationship
For example
if ( $reply->repliable_type == 'SomeName' ){
// activity should not be recorded
}
Now i'm wondering if i should create a method isRecordable
for all models that i want to record the activity
public function isRecordable()
{
return true;
}
And for the Reply model it would be
public function isRecordable()
{
if ( $this->repliable_type == 'SomeName' ){
return false;
}
return true;
}
}
or should i create a class that determines if the model is recordable
For example
class DetermineRecordability
{
public function handle($model)
{
if ( class_basename($model) == 'Reply' && $model->repliable_type == 'SomeName'){
return false;
}
return true;
}
}
Which approach do you think is better ?
Replied to Get The Value That Failed The Validation Rule
@automica thanks!
this is what i am doing but in my case
i have an array as input and each value in the array must exist in the database.
If one of the values in the array does not exist, then the validation will fail and
what i am looking for is to grab that value that failed the validation.
For example let's say that the input consists of the array
['john', 'doe']
and ```doe`` does not exist in the database
i want to be able to concat the message with the value doe
so the message error that i want is
The following members could not be found: doe
Replied to Get The Value That Failed The Validation Rule
@jlrdw thanks!
What i want is to add the value that failed the validation in the error message and not to display it in the view. ( i want to return it as a json response )
However, the $errors
variable displays only the error that occurred and not the value that caused the error.
Started a new Conversation Get The Value That Failed The Validation Rule
I have a FormRequest with the following rule.
It takes an array as input and for each value in the array the following rules are applied.
public function rules()
{
return [
'participants.*' => ['required', 'string', 'exists:users,name'],
];
}
What i want, is to get the value from the array that failed the validation rule and use it in the error message.
What i have tried is to use the :attribute
which returns a string with the name of the array and the index of the value that failed the validation.
So for example if the array participants has the following values
participants = ['john', 'doe']
and the value doe does not exist in the database, then the :attribute
will return the string participants.1
And i can use the index from the resulting string to get the value from the input array.
public function messages()
{
return [
'participants.*.exists' => 'The following recipients could not be found: :attribute ,
];
}
I was wondering if there is a more elegant way to get the value that failed the validation.
Replied to HasMany < - > HasOne Relationship Fail ->What Did I Miss
public function teammates()
{
return $this->belongsToMany(User::class, ‘user_id’);
}
Make sure that in your team table, you have a user_id
Replied to Repetitive Validation Rules And Messages
the rules required and string are used on several validations and the error message is always the same.
i want to avoid writing the same rule and error message in multiple places
instead i was looking for a solution where the rule and the message are stored in one place ( thus a rule named BodyIsRequired ( but maybe what i'm want to do is "too much" and i can just write the same rule and message since they are quite simple )
I agree that it would be better to read the rules and understand them without reaching another file.
Started a new Conversation Repetitive Validation Rules And Messages
I'm trying to come up with a solution to avoid writing the same validation rules and messages over and over.
For example i have the following rules for the body
attribute
public function rules()
{
return [
'body' => ['string','required'],
];
}
I want the validation message for both rules to be the same.
One approach is to create two custom rules and, string and required and write the message that i want in each rule.
class BodyMustBeString implements Rule
{
public function passes($attribute, $value)
{
if (is_string($value)) {
return true;
}
return false;
}
public function message()
{
return 'Please enter a valid message.';
}
}
class BodyIsRequired implements Rule
{
public function passes($attribute, $value)
{
if (empty($value)) {
return false;
}
return true;
}
public function message()
{
return 'Please enter a valid message.';
}
}
The other approach is to create one custom rule for the body attribute attribute that consists of both string and required rules.
class BodyRules implements Rule
{
public function passes($attribute, $value)
{
if ( !empty($value) && is_string($value)) {
return true;
}
return false;
}
public function message()
{
return 'Please enter a valid message.';
}
}
Do you have any other approach or do you agree with any of my approaches ?
Replied to Errors Are Available In View But Not In Test Assertion
Ok, thanks!
By the way, if could suggest a different approach in case you think that my approach is "wrong" or unusual, please let me know.
Replied to Errors Are Available In View But Not In Test Assertion
Yes that is what i am doing.
And the errors are displayed correctly in the view, i just couldn't assert that the session has errors in my test.
More specifically, in my search endpoint what i do is
public function show(Search $search, SearchRequest $searchRequest)
{
$validator = $searchRequest
->validate($this->request)
->getValidator();
if ($validator->fails()) {
return view('search.show')
->withErrors($validator);
}
$results = $search->handle($this->request);
$query = $this->request->input('q');
return view('search.show', compact('results', 'query'));
}
NOTE
The SearchRequest
does not extend the FormRequest
, it just uses the Validator
facade to apply the validation rules that i have defined.
Replied to Errors Are Available In View But Not In Test Assertion
ok that's what i'm doing wrong :)
Is it considered wrong to validate the request parameters when you make a GET
request ?
In my case, i make a search request using GET
and before i start searching i want to validate the search parameters.
Started a new Conversation Errors Are Available In View But Not In Test Assertion
I'm using a custom validation and if the validation fails i return a view with the errors
validator = Validator::make(
$request->input(),
$this->rules($request),
$this->messages()
);
if ($validator->fails()) {
return view('some-view')
->withErrors($validator);
}
The issue that i have is that even though in the view i do have access to the errors
@if($errors->any())
@foreach($errors->all() as $error)
{{ $error }}
@endforeach
@endif
When i try to assert that the session has errors, it fails.
$this->get('path')->assertSessionHasErrors('error-name');
but if i assert that the error message is being displayed in the view, it works. And indeed the errors are being displayed when i use the browser.
$this->get('path')->assertSee('error message');
Any thoughts on what i might be doing wrong ?
Replied to How To Sanitize Html Content From Database ? PHP Native
Have a look at this video
https://laracasts.com/series/lets-build-a-forum-with-laravel/episodes/101
Replied to Return To View With Errors If Form Request Validation Fails
is the only way to make this work to create a new endpoint that only shows the search results or errors ?
class SearchController extends Controller
{
public function show(Search $search, SearchRequest $searchRequest)
{
$results = $search->handle();
return redirect(route('search-results.show', compact('results'));
}
}
class SearchRequest extends FormRequest
{
public function failedValidation(Validator $validator)
{
return redirect(route('search-results.show')->withErrors($validator);
}
}
Started a new Conversation Return To View With Errors If Form Request Validation Fails
I use a form request named SearchRequest to validate the query parameters and in case the validation fails, I want to return to search.show view in order to display the errors.
If the validation does not fail, then execute the search and return to search.show to display the results.
class SearchController extends Controller
{
public function show(Search $search, SearchRequest $searchRequest)
{
$results = $search->handle();
return view('search.show', compact('results');
}
}
class SearchRequest extends FormRequest
{
public function failedValidation(Validator $validator)
{
return view('search.show')->withErrors($validator);
}
}
When the validation fails, it does return the search.show view but the errors are not available
search.show.blade.php
@if($errors->any())
@foreach($errors->all() as $error)
<p> {{ $error }} </p>
@endforeach
@else
<p> {{ $results }} <p>
@endif
I have also tried to redirect instead of returning a view in the failedValidation method
public function failedValidation(Validator $validator)
{
return redirect(route('search.show'))->withErrors($validator);
}
But for some reason the first time that I hit the endpoint, it does not show the errors. I have to refresh to display the errors and I don't get why that happens.
Any thoughts ?
Replied to Return Json Response Or A View
In the case where there is only a search bar ( a single input ) then yes it looks nice to display the results on the same page.
However in the case where of a search form ( multiple inputs ) it does not look very nice to me at least because the search form already takes a lot of space.
You are right it would be better to stick with either Ajax or search form. But i saw this mix on Macrumors and I wanted to create the same thing and I was not sure how to do it.
So the point was to use a search form but in case something goes wrong, to stay on the same page and at the same time give some feedback to the user, which can be achieved using Ajax
Replied to Return Json Response Or A View
The reason I want to return a view is because the current page is a search form and if I return json data I will have to show in the same page the search form and the results of the search which will be a mess.
That’s why I want to go to a different view to display the search results
Started a new Conversation Return Json Response Or A View
I have a search functionality and i want to return either
A json response which consists of an exception message in case no results are found or an exception is thrown during search
a view with the search results if everything works properly
currently the only way i can think of dealing with this is
Search.vue
search(){
axios.get('/search/' + queryParameters)
.then(() => this.showResults())
.catch((error) => flash(error.message));
},
showResults(){
location.href = '/search/results' + queryParameters;
}
And in laravel i will have the following routes
First make a search and if any exceptions are thrown ( the exceptions are thrown by the Search class ) then a response message is returned ( the response message is handled in Handler.php )
Route::get('/search', function(Request $request, Search $search){
$search->handle($request);
});
If results are found and no exceptions are thrown then send a request to the following route
Search and return a view with the results ( this route is called by the showResults method in Search.vue
Route::get('/search/results', function(Request $request, Search $search){
$results = $search->handle($request);
return view('search.show', compact('results');
})
Is there an alternative to what i'm trying to do ?
Because right now in order to make a search and get results, i have to send 2 requests and search 2 times.
Started a new Conversation Handle Response Message In Front Or Back End ?
When i make a request from the front end to the back end ( post, get etc) with axios for example
I can use then and catch to handle either a successful or a failed request and flash a message in the front end to give feedback to the user.
For example.
axios.get('/apples')
.then((response) => flash("You got the apples"))
.catch((error) => flash("You didn't get the apples"));
Or i can write the messages in the back end and then use them to flash the messages in the front end.
For example
axios.get('/apples')
.then((response) => flash(response.message)
.catch((error) => flash(error.message));
My question is whether there is a reason to prefer either one, or it does not matter ?
Replied to Optimize Query
I think that neither the distinct nor having methods optimize the query. The method distinct as the name indicates, it gets rid off the duplicates and the having method has the same functionality with the where method. Therefore it depends on what results you need.
Replied to Nested Foreach
Where are the buttons ?
What is the structure of the fields ? Probably you see extra buttons because each field has more elements than you expect it to have
Replied to Fetch One Record From One To Many Relationship
@tykus thanks!
wouldn't it be redundant to have the same user_id in both the conversations table and the conversation_participants table for the same conversation record ?
Started a new Conversation Fetch One Record From One To Many Relationship
I have the following tables
users
- id
conversations
- id
conversation_participants
- id
- conversation_id
- user_id
Now each conversation is created by a user.
What i'm looking for, is to create a relationship to get the user who started the conversation.
I've tried to do that using a belongsToMany relationship and fetch only 1 record. But it returns a collection, while i need a model.
class Conversation extends Model
{
public function starter()
{
return $this->belongsToMany(
User::class,
'conversation_participants',
'conversation_id',
'user_id'
)->orderBy('created_at','asc')->take(1);
}
}
Is there a way to create such a relationship ?
Replied to Tailwind Css- How To Arrange Few Items On Same Row?
I think that flex box is what you are looking for. Have a look at the tailwind docs
Replied to Associate Prepare Data For Validation With Rule Error
Now that i'm reading again your suggestion.
The reason is that the input consists of usernames which must exist in the database. Therefore, i can't validate a string with multiple usernames.
Another reason is that a comma is included only when multiple usernames are passed.
What i did in the prepareForValidation method is
public function prepareForValidation()
{
if (!is_string($this->names)) {
return;
}
if (str_contains($this->names, ',')) {
$this->request->merge(
[$this->attribute => $this->splitNames($this->names)]
);
} else {
$this->request->merge(
[$this->attribute => [$this->clean($this->names)]]
);
}
}
And the validation rules are
public function rules()
{
return [
'title' => ['required', 'string', 'min:3'],
'message' => ['required', 'string'],
'participants' => ['required', "array", 'min:1'],
'participants.*' => ['required', 'string', 'exists:users,name'],
];
}
So i expect the input to be string. If the input is string then check if comma symbol is included and if yes then explode. Otherwise only one username is in the string and therefore create an array with a single username.
The validation rules expect an array. If the input is not a string then prepareForValidation will not create an array and the validation will fail. If the input is a string, then an array will be created as expected and then the validation rules will check if the content of the array are string and if each element ( username ) exists in the database.
Replied to Associate Prepare Data For Validation With Rule Error
I wasn’t sure if it makes sense to assume that it might not be string.
Thanks for the suggestion
Started a new Conversation Associate Prepare Data For Validation With Rule Error
I have a FormRequest with the following rules.
public function rules()
{
return [
'participants' => ['required', 'array', 'min:1'],
'participants.*' => ['required', 'string', 'exists:users,name'],
];
}
However the value of the attribute participants comes from an input text and is a string which normally it can be a single name or multiple names separated with comma.
'George'
'George, John'
And before i validate the data i have to manipulate it.
public function prepareForValidation()
{
request()->merge(['partcipants' => explode(",", request('participants')])
}
Does it make sense to assume that the value of the participants might not be a string ?
So in case an object or an array is passed to participants
Then the prepareForValidation will fail and i can't even associate it with the participants attribute.
One way to solve this would be to create a new rule in order to be able to associate any kind of exceptions with the participants attribute.
However, in my application i assume that i receive an array from the participants ( an array of usernames ) parameter and i don't find a good idea to hide the prepareForValidation ( the functionality that splits the string into an array ) inside the new rule.
Any thoughts on how i can prepareDataForValidation, associate any failure with the rule so i can return the corresponding message, and at the same time avoid hiding the split of string to array inside a rule.
Replied to Manipulate Request Parameters
exactly.
i want to change the value of a request parameter and validate it independently.
thanks again, i will check this out
Replied to Manipulate Request Parameters
thanks for the suggestion.
if i wanted to apply this particular manipulation to different routes though, how should i approach it ?
Because the user can enter comma separated names in different forms where each form consists of different request parameters.
Therefore if i manipulate the request parameter in the prepareForValidation method, i would end up repeating the code for manipulating se same request parameter.
Started a new Conversation Manipulate Request Parameters
I have an input text in which a user can enter a single username or comma separated usernames.
I have a rule that validates whether the usernames exist. In that rule if the string consists of comma separated usernames i create an array of usernames and then validate the array.
class CommaSeparatedUsernames implements Rule
{
public function passes($attribute, $value)
{
if (str_contains($value, ',')) {
$this->validateMultipleUsernames($attribute, $value);
}
return $this->validateUsername($attribute, $value);
}
}
However, the value in the request parameter remains the same ( a string which consists of comma separated usernames ) and then i have to split again the string into an array in order to use the usernames.
Right now i have this function within the rule
class CommaSeparatedUsernames implements Rule
{
public function passes($attribute, $value)
{
if (str_contains($value, ',')) {
$this->validateMultipleUsernames($attribute, $value);
$this->replaceParticipantsParameter($value);
}
return $this->validateUsername($attribute, $value);
}
public function replaceParticipantsParameter($commaSeparatedNames)
{
request()->merge(
['participants' => $this->getNames($commaSeparatedNames)]
);
}
public function getNames($commaSeparatedNames)
{
return array_map(
fn($name) => trim($name, ' '),
explode(',', $commaSeparatedNames)
);
}
}
My question is where should i put the code for manipulation the request parameter
Does it make sense to create a middleware that manipulates the request parameter ?
Should i put that code in the Controller or in the Model that uses the request parameter ?
Started a new Conversation Laravel Controller Methods Naming Convention Alternatives
I have a FollowController and i want to
However i can't do that in the same controller without breaking the naming convention or without introducing if statements in the controller.
I would like to know your opinion on this.
First Approach
Create separate controllers
class FollowersController extends Controller
{
public function index(User $user)
{
return $user->followedBy;
}
}
class FollowingController extends Controller
{
public function index(User $user)
{
return $user->follows;
}
}
Second Approach
Break the naming convention and fit both in the same controller
class FollowController extends Controller
{
public function followedBy(User $user)
{
return $user->followedBy;
}
public function follows(User $user)
{
return $user->follows;
}
}
Third Approach
Use a flag to decide whether to return either one or both ( in this case 1 extra request is avoided and you serve both in a single request )
class FollowControllerr extends Controller
{
public function index(User $user)
{
if (request()->boolean('follows'))
{
return $user->follows;
}
elseif (request()->boolean('followedBy'))
{
return $user->followedBy;
}
return [
'follows' => $user->follows,
'folllowedBy' => $user->followedBy
];
}
}
Replied to Instantiation Of Chained Filters
@bugsysha that cleaner indeed.
initially what i wanted to do is something like
public function index(ThreadFilters $filters)
{
$filters->apply($someBuilder);
}
So the controller wouldn't have to know anything except for applying the filters.
But unfortunately since i had to chain Filter in some cases i created the FilterManager and then i couldn't use the aforementioned approach.
However, your suggestions is much better than my final approach. so thank you again
Replied to Instantiation Of Chained Filters
@bugsysha no there are no default filters in FilterManager
The FilterManager exists to find the requested filters that are passed from an HTTP request ( the requested filters correspond to methods in Filter classes ) . I then manually chain the required Filter classes depending on where i need the FilterManager and finally i apply the filters using the FilterManager
Replied to Instantiation Of Chained Filters
@bugsysha oh now i understand your question.
Because in some cases i might need to add 2 different Filter classes
for example
$filterManager->addFilter(new ThreadFilters(());
$filterManager->addFilter(new PostFilters());
Then when i call
$filterManager->apply($someBuilder);
I apply the filters from both Filter classes. In addition to that, in some cases these Filter classes have some methods in common that are inherited from another Filter class and in the FilterManager i prevent applying the same filter twice ( which i don't know if it makes much sense since the filters are actually SQL where statements which i guess are not costly )
Replied to Instantiation Of Chained Filters
@bugsysha it's not quite clear to me what do you mean by "why".
In the ThreadController and more specifically in the method index, i know that i need the ThreadFilters, that's why i add the filter ThreadFilters. In other places i might need a different filter. And in some cases i need two different Filter classes
class ThreadController extends Controller
{
public function index(FilterManager $filterManager)
{
$filterManager->addFilter(new ThreadFilters());
// here i know that i need the ThreadFilters
}
}
Replied to Instantiation Of Chained Filters
@bugsysha thanks again! any suggestions for a better approach are appreciated :)
Replied to Instantiation Of Chained Filters
@bugsysha thanks. Isn't an issue that i have to manually
$filterManager->addFilter(new ThreadFilters());
where ThreadFilters i guess is also a dependency that is hidden.
or it is even better to do the following
class ThreadController(FilterManager $filterManager, ThreadFilters $threadFilters)
{
$filters = $filterManager->addFilter($threadFilters);
$filters->apply($someBuilder);
}
Started a new Conversation Instantiation Of Chained Filters
I have different Filter classes and each class has a number of filter methods. There is ( so far ) one case where I need to apply filters from 2 classes. What I did is that I created another class named FilterManager which stores a list of all the Filter classes you need and then it iterates over the list and applies the filters.
class ThreadFilters
{
protected $builder;
protected $supportedFilters = ['newThreads'];
public function newThreads($userId)
{
$this->builder->orderBy('created_at', 'DESC');
}
}
class FilterManager
{
protected $filters = [];
public function addFilter($filter)
{
$this->filters[] = $filter;
}
public function apply($builder)
{
foreach($this->filters as $filter)
{
$filter->builder = $builder;
foreach($filter->supportedFilters as $filterMethod)
{
$filter->$filterMethod()
}
$builder = $filter->builder;
}
}
}
Now in the controller I can do something like
First Approach
class ThreadController(FilterManager $filterManager)
{
$filters = $filterManager->addFilter(new ThreadFilter());
$filters->apply($someBuilder);
}
On the other hand I could use the app function
app(ThreadFilters::class)
and in the service container basically I could do
$this->app->bind(ThreadFilters::class, function($app){
$filterManager = new FilterManager();
$filterManager->addFilter(new ThreadFilters());
return $filterManager;
});
and therefore my controller would look like
Second Approach
class ThreadController()
{
$filters = app(ThreadFilters::class);
$filter->apply($someBuilder);
}
My question is which solution is good/acceptable.
In the First Approach the FilterManager dependency is injected and the controller has to add the ThreadFilters
In the Second Approach no dependencies are injected and the controller has to use the service container to hide the specifics and make the controller has less knowledge about the filters.
Replied to Factory That Depends On Request Parameters
@rodrigo.pedra that's a very nice idea, thank you
Replied to Factory That Depends On Request Parameters
@automica hahaha oh no. is just a simple example to illustrate the question
Replied to Factory That Depends On Request Parameters
i have updated the question ( basically is a question on top of the initial question ).
To make sure that i'm not on the wrong way
Started a new Conversation Factory That Depends On Request Parameters
Given that I have a factory class that create fruits based on a request parameter.
Should the client know the exact parameter that needs to be passed to the factory ( First Approach)
Or it is better to pass a generic parameter to the Factory ( in other words hide the specifics from the client ) and let the Factory know which exact parameter it needs to create an object. ( Second Approach )
First Approach
class FruitFactory
{
public function create($type)
{
if($type == 'Apple')
{
return new Apple();
}
}
}
$fruitFactory = new FruitFactory();
$type = request('type');
$fruit = $fruitFactory->create($type);
Second Approach
class FruitFactory
{
public function create(Request $request)
{
$type = request('type');
if($type == 'Apple')
{
return new Apple();
}
}
}
$fruitFactory = new FruitFactory();
$fruit = $fruitFactory->create();
Updated Question
If the FruitFactory is a factory of factories, does it need to know about all the exact parameters that its children factories need to create an object ?
Or maybe in this case the second approach is better ( Just passing the general $request parameter and let the factory find out which parameter it needs to create an object )
class FruitFactory
{
public function create($type, $color)
{
if($type == 'Apple')
{
return app(Apple::class)->create($color);
}
}
}
class Apple
{
public function create($color)
{
if($color == 'green')
{
return new GreenApple();
}
}
}
$fruitFactory = new FruitFactory();
$type = request('type');
$color = request('color');
$fruitFactory->create($type, $color);