Unknown column 'subscriptions' in 'where clause' - laravel

I am using this subscription package https://github.com/overtrue/laravel-subscribe , so i want to get thread (post) associated with the space a user has subscribe to
$spaces = Space::whereHas('subscribers', function($query){
$query->where('subscribable_id', '=', 'user_id');
})->with('thread')->orderBy('id', 'desc')->paginate(10);

I have very recently come across a question asking almost the same thing. So I'll plagiarize my own answer in hopes it can be tailored for you as well.
To get the currently logged in user, we can use the auth()->user() helper function. Then we must get the spaces the current user is subscribed to, so if you have a relationship in your User model, it would be perfect and you can use the following:
$space = auth()->user()->spaces;
Otherwise, you can use your code from your question to get the spaces the user is subscribed to, but I'd still advise creating a method for this inside the user model:
$spaces = Space::whereHas('subscribers', function($query){
$query->where('subscribable_id', '=', 'user_id')->get();
We got the spaces, but what you need is the threads of those spaces, so we must add in more logic where we fetch the IDs of those spaces you follow, then retrieve all the threads that those spaces contain to then perform whatever queries you need at the end.
$spaceIds = $spaces->pluck('id');
$threads = Thread::whereIn('space_id', $spaceIds)->latest()->paginate(10);
I haven't tested this code since I don't have the same structure, so you may need to debug a thing or two to fix the queries but it should theoretically perfectly match your structure.

Related

Laravel - trouble with Eloquent Collection methods such as last()

I have a variable $courses that in my debugger is shown as type App/Courses
In the model, there is a one to many relation and I retrieve these related items in the model and then access as $courses->relateditems
In the debugger, $courses->relateditems is shown as type Illuminate/Database/Eloquent/Collection
Ok, all makes sense.
I want to get last item in the $courses->relateditems collection. So I try
$courses->relateditems->last()->startdate
But this is not returning the value that I know exists. And when I evaluate the expression $courses->relateditems->last() in the debugger I get this in my laravel.log:
Symfony\Component\Debug\Exception\FatalErrorException: Cannot access self:: when no class scope is active in /app/Courses.php:68
I am just not sure what is going on. I know I can use DB queries to just get the data I need, but I have a model event triggering a function and that function receives the $courses object/model (or however we name it) and I am just trying to get this working as above.
Ideas on what I am doing wrong?
thanks,
Brian
The issue here based on the error you have at the bottom of your post is a mistake in your code.
However, I think you've misunderstood how Laravel relationships work. They essentially come in two forms on a model, that of a property and that of a method.
relateditems Is a magic property that will return the result of a simple select on the relationship if one has already been performed. If one hasn't been performed it will perform one and then return it, known as eager loading.
relateditems() Is a method that returns a query builder instance for that relationship.
By calling relateditems->last() you're loading ALL related items from the database and then getting the last, which is less than optimal.
The best thing for you to do is this:
$course->relateditems()->orderBy('id', 'desc')->first();
This will get the first item return, however, since we've ordered it by id in descending order, it'll be reversed from what could be considered its default ordering. Feel free to change the id to whatever you want.

How can I chain ->makeHidden() when eager-loading a belongsToMany relationship?

Does anyone have any suggestions for how to use makeHidden() when eager-loading? here is my code:
$work=Work::with([
'work_category'=>function($query){
$query->with(['companies'=>function($query){
$query->select('companies.id','companies.name');
}]);
},
'prices',
])
->findOrFail($id);
Work has a belongsTo('App\WorkCategory') relationship, and WorkCategory has a belongsToMany('App\Company') relationship through a pivot.
If I try to chain ->makeHidden('pivot') on to $query->select('companies.id','companies.name'); - I get a BadMethodCall exception: Call to undefined method Illuminate\Database\Eloquent\Relations\BelongsToMany::makeHidden()
Is there something I'm missing here?
This is the code with the offending makeHidden() call
$work=Work::with([
'work_category'=>function($query){
$query->with(['companies'=>function($query){
$query->select('companies.id','companies.name')->makeHidden('pivot');
}]);
},
'prices',
])
->findOrFail($id);
My temporary fix has been to add protected $hidden=['pivot']; to my Company model, however it would be nice to be able to access the pivot when I do need it, and use $model->relation->makeHidden('attribute') in my controllers to trim away extra data before sending.
Unfortunately, makeHidden() doesn't work on relationships in Laravel. Not directly nor using dot notation on the related field.
You've touched upon one solution that I've used in the past, which is to use select() to limit to only the relationship's fields that you want within the subquery as a somewhat rough way to exclude your pivot:
$query->with(['companies'=>function($query){
$query->select('id','name', 'something', 'something');
}]);
This works when the fields are limited. But its a pain when you have a lot or are making many queries.
Another alternative is to do what you've done, and mark it protected on the model: protected $hidden=['pivot'];. You then have some flexibility in various methods to use ->makeVisible('pivot'); on the fly to regain access to this pivot.

I want to check duplication value during insert time without using unique keyword

i make one table for with some column with nullable.
i already tried with two different query. one using
Register_member::where('passport',$passport)->orWhere('adharcardnumber',$adharcardnumber)->get();
and second DB::table type query.
$row = Register_member::where('passport',$passport)->orWhere('adharcardnumber',$adharcardnumber)->get();
if (!empty($row))
{
return response()->json(["status"=>0, "message"=>"Adharcard or Paasport number already exit."]);
}
if (empty($row))
{
Register_member::insert(['first_name'=>request('first_name'), 'middle_name'=>request('middle_name'), 'last_name'=>request('last_name'), 'adharcardnumber'=>request('adharcardnumber'), 'ocipcinumber'=>request('ocipcinumber'), 'passport'=>request('passport'), 'birthday'=>request('birthday'),
'mobilecode'=>request('mobilecode'), 'mobilenumber'=>request('mobilenumber'), 'email'=>request('email'), 'address'=>request('address'), 'landmark'=>request('landmark'), 'area'=>request('area'),
'gender'=>request('gender'), 'pincode'=>request('pincode'), 'city_name'=>request('city_name'), 'state_id'=>request('state_id'), 'country_id'=>request('country_id'), 'sampraday'=>request('sampraday'), 'other'=>request('other'), 'sms'=>request('sms')]);
return response()->json(["status"=>1, "message"=>"Member register successful."]);
}
if adharcardnumber or passport number are exists in table, then nagetive response. if in both any one in unique then, insert data in table
Let me suggest you something which I think serve you as a good solution. You can use the unique with required and regex. In this way it will use the already recommended ways of Laravel which are the best.
As an example for your adhaar card,
the validation should look like this
$request->validate([
'adhaar ' =>['required','unique:users','regex:/\d{12}/'],
]);
where adhar is the filed name where adhaar number is entered. Be sure to use validator like this use Illuminate\Support\Facades\Validator;. Also $request is the instance of the Request.
Using the required prevent empty field submission and the regex will throw an error if the pattern is not matched completely. so I think it would be a better a way to handle the scenario.
the above solution will work in both adhaar and passport. But for the passport the regex will be different though.
Please note these are all demo examples, you might need to modify it according to your needs. I use https://www.phpliveregex.com/ for regex making and checking and it is good enough.
I hope you get an idea of how to begin but if you need more information then let me know in the comments.

Why does Laravel Controller needs (integer) cast in Homestead, but not in production server

(integer) cast must be done in Homestead for Controller parameter
I am having a hard time searching for the cause of a discrepancy between my local dev environment (Homestead) and the hosted one.
I define a route like this:
Route::get('group/{id}/data', 'GroupDataController#index');
And the code in the Controller looks like this:
public function index($id)
{
return Grouping::all()->where('group_id', $id);
}
Which works fine in production (hosted env), BUT when I execute it locally it throws and empty array [] unless I modify my Controller function to look like this:
public function index($id)
{
return Grouping::all()->where('group_id', (integer)$id);
}
I have no idea of what is going on in here, and I am tired of making changes all over my Controller to make it work on both environments.
I have searched in several place, but maybe I am using incorrect tokens for my search as I have found nothing.
Any help will be really appreciated.
The problem here is that you're not using the correct set of functions.
When you call Grouping::all(), this is actually returning an Eloquent Collection object with every record in your groupings table. You are then calling the where() method on this Collection. This causes two problems: one, it is horribly inefficient; two, the where() method on the Collection works differently than the where() method on the query builder.
The where() method on the Collection takes three parameters: the name of the field, the value in that field on which to filter, and finally a boolean value to tell it whether or not to do a strict comparison (===) or a loose comparison (==). The third parameter defaults to strict. This strict comparison is why you're having the issue you are having, but I cannot explain why one environment sees $id as an integer and the other doesn't.
The where() method on a query builder object will actually add a where clause to the SQL statement being executed, which is a much more efficient way of filtering the data. It also has more flexibility as it is not limited to just equals comparisons (the second parameter is the comparison operator for the where clause, but will default to "=" if it is left out).
You have two options to fix your issue. You can either pass in false as the third parameter to your where() method in the current code (bad), or you can update the code to actually filter using the query instead of filtering on the entire Collection (good).
I would suggest updating your code to this:
public function index($id) {
return Grouping::where('group_id', '=', $id)->get();
}
In the above code, Grouping::where('group_id', '=', $id) will generate a query builder object that has the given where clause, and then get() will execute the query and return the Collection of results.
I marked #patricus (thanks you, so much!) as the correct answer, for he really pointed me in the right direction to understand that there are some keywords that work differently under different contexts (like get()), but I will also point out, how my 2 confusing points were solved in my case:
The difference in my code between production and Homestead development environments was solved by pointing my Homestead to the production database. I am not sure what was the difference (maybe collation or table format), but it gave me a quick out.
I was trying to filter a list of elements in the database but I was constructing it with the wrong logic for Laravel.
To clear what I refer to in the second point, I was using this code:
Grouping::all(['id', 'name_id', 'product_id', 'ship_id'])->where('name_id', '=', $id);
I thought this could work, because it would be selecting all items, with the selected columns, and then filter those with a where clause. But I was wrong, since, as I found out later, the correct way of writing this is:
Grouping::where('name_id', $id)->select('id', 'name_id', 'product_id', 'ship_id')->get();
This is because I forgot completely that I was assembling the query, not writing the actions I expected the program to do.
This second syntax has more logic, since I specify the filter, then put the columns over what was filtered, and finally execute the query with the get() clause.
Of course, it can also be written the other side around for clearer fluent reading:
Grouping::select('id', 'name_id', 'product_id', 'ship_id')->where('name_id', $id)->get();

CakePHP soft deleted data still showing up in model associations

I am using the SoftDeletableBehavior for my addresses model.
This sets it so when you delete an address it doesnt really delete it, instead sets a deleted column in the database to 1.
This also changes your query in the beforeFind function of the Address model to only pull entries where deleted != 1
This works when I am viewing addresses via the addresses controller like /addresses/show/43.
However my Users controller hasMany addresses. So when I am in the users controller and I call $this->find('first'); To get the user, I also get the associated addresses. I am expecting this to NOT give me the (softly) deleted addresses but it DOES. The beforeFilter on the Address Model is never called either.
What is the right way to handle this?
Update.
Apparently when I do this:
$data = $this->User->find('first',array('conditions' => array('User.id' => $id),'recursive' => 2));
I have a $data['User] array and a $data['Address] array (along with others).
But this does not use the beforeFind filter in the Address Model. So I get the wrong data.
But if I do this:
$data = $this->User->find('first',array('conditions' => array('User.id' => $id),'recursive' => 2));
$data['Address'] = $this->User->Address->find('all',array('conditions'=>array('user_id'=>$id)));
Then it does use the beforeFind filter on the Address model and returns the right data.
(Of course in a different format [Address][0][id] vs [Address][0][Address][id])
What I do not understand is if the associated data thats pulled does not use its own model to find its data, then whats the point? Isn't this one of the main purposes of MVC?
Maybe I just don't understand? Or I am missing something?
Can someone please enlighten me?
Can't you put this condition in your association?
<?php
class User extends AppModel {
var $hasMany = array(
'Address' => array(
'conditions' => array('NOT' => array('Address.deleted' => '1')),
)
);
}
?>
Personally, I would add a condition to the find query for users
$this->Users->find->('all',array('conditions'=>array('Address.deleted !='=>'1')));
(haven't tested the code for exact syntax, but this is the general idea)
When you use $this->User->find(...) and include Addresses, you will only see the User model's callbacks fire, because that is the model you are using to drive the find.
This is why you have to use extra conditions in your User model's associations to filter out the soft-deleted Addresses.
If the Address model's callbacks were fired during every find on the User model when recursion scope included Addresses, you'd have no control as to when to include the soft-deleted Addresses and would risk clashes in functionality and param manipulation by those callbacks. Not to mention the serious performance impact this could have.

Resources