I have been trying to use a Boolean value in laravel TINYINT(1). However although it works fine for true/1 it cannot understand false/0. So if I change it to false it will still read as true as it seems Eloquent ignores the false value.
I have tried strings, ints and normal Boolean but they all have the same effect.
// I pull in the info kind of like this
$fields = Input::all();
$model->never_expires = $field['never_expires'];
$model->save();
never_expires is fillable within the model and uses a laravel boolean (created from the schema generator) which translates to a TINYINT(1).
I'm sure its something super simple I am missing, but I just can't see it.
I found that only by manually adjusting the Boolean row it would actually update, so I added the following to the model.
public function setExpire($never_expires)
{
if ($never_expires == 1)
{
DB::table($this->table)->where('id', $this->id)->update(['never_expires' => 1]);
}
else
{
DB::table($this->table)->where('id', $this->id)->update(['never_expires' => 0]);
}
return true;
}
protected function getHasExpireAttribute()
{
return $this->never_expires;
}
Basically I used the public function setExpire to handle adding the expiry and use has_expire as a fake element to get it (to ensure that I know its only my function saving anything to that column).
Very dirty way to do it since you basically do 2 mysql queries in one save, but it works.
I won't be using this answer as the answer, I still believe there must be a better way, but spent too long on such a tiny problem.
Related
EDIT:
I outputted the array and #apokryfos had mentioned something about resources not being able to be serialized.
Here is how some debug output looks: (Removed some information that is more sensitive)
Stream in Timestamp
It is my timestamp causing the issue. If I do
unset($user["timestamp"]);
Then almost everyone's solution works. So, the real reason was the resources was in there for my timestamp. How do I stop or fix that? I tried to do
public $timestamps = false;
This did not have any changes.
I have read through the documentation and a few tutorials. Sadly, I can't seem to find any documentation on what is returned and what functions are available for use when using Eloquent. Maybe I am just missing it.
However, I am using this code in my controller.
public function find($userName){
$user = UserSecurity::where('userName', $userName)->get();
//dd($user);
return Response()->json(['data' => $user], 200);
}
This is my router code.
$router->get('/UserSecurity/find/{userName}', ['uses'=>'UserSecurityController#find']);
I know that it is pulling the correct data from the database as if I uncomment the dd($user) I can see it on screen. However, if I try to send a response through Response()->json(..) it fails with this screen.
Image of Exception
I know I am probably using Response() incorrectly, but I have actually tried a number of different ways. Some just show empty responses and some crash similarly.
I have tried removing get() which from what I have found just returns nothing as there are no results. I have tried Response($user) and it was empty. I have tried return Response()->json($user); with the same type unsupported error.
Thanks for any help.
EDIT:
Changing a few code for testing. I changed to this
public function find($userName){
$user = UserSecurity::where('userName', $userName)->get()->toJson();
$user = json_encode($user);
return Response($user);
}
This returns false . I am not sure where the boolean is coming from. The original dd($user) actually has the correct information from the DB, so I know it is doing the query correct.
I think you must add the following in the top of your controller
then this code will help you.
use Illuminate\Support\Facades\Response;
The error message Type is not supported is actually coming from PHP's JSON serializer. There are only a very few types that PHP cannot serialise and the one that seems to be causing the issue in your particular case is a stream resource.
In order to check what is actually serialized in your model you will need to call:
$user = UserSecurity::where('userName', $userName)->get();
$user->map->jsonSerialize()->dd();
jsonSerialize exists because all Laravel models implement the JsonSerializable interface.
This will dump a collection of arrays of what PHP will attempt to serialise as JSON. The contents of this array are recursively serialised.
The default implementation of JsonSerializable in Model will attempt to serialize all model attributes but first will attempt to cast and call all accessors on attributes. This may or may not cause issues. At any rate the solution here is to figure out why there's a resource being returned in the jsonSerialize method and figure out the best way to hide it. Normally you can hide attributes by using
protected $hidden = [ 'timestamp' ];
however from your question it seems that the answer may not be so straight forward so may need to dig deeper.
I think you must add the following in the top of your controller:
use Illuminate\Support\Facades\Response;
and the controller must be like:
public function find($userName){
$user = UserSecurity::where('userName', $userName)->get();
return Response::json($user, 200);
}
PLease try like this
return response()->json(['status' => true, 'data' => $user],200);
I am already using this in my code
Using where('...')->get() returns a collection which cannot be used in response()->json().
Try using:
public function find($userName){
$user = UserSecurity::where('userName', $userName)->first();
return response()->json(['data' => $user->toArray()], 200);
}
here is just a typo error, you must write response() in lowercase instead or Response() because Response is a class, while response() is a magic Laravel function which already instantiates the class Response, and that you can use to return a Response instance.
I'm trying to figure out how to properly code the update() function in eloquent to return either 0 or 1 based on user input in a form. For example, if I hit the update button without making any changes, it returns 1. Shouldn't it return 0?
I tried researching for solutions like here in stackoverflow to see if anyone has the same problem as I am facing. But so far not luck. I also tried modifying the code, but no luck.
public function update(Request $request, $id)
{
$UCPost = UCPost::find($id);
$UCPost->gown_2019 = $request->input('Gown2019');
$UCPost->gown_2017_2018 = $request->input('Gown20172018');
$UCPost->gown_2016 = $request->input('Gown2016');
$UCPost->gown_2015 = $request->input('Gown2015');
$UCPost->Light_Blue = $request->input('LightBlue');
$UCPost->Seconds = $request->input('Seconds');
$UCPost->Velveteen = $request->input('Velveteen');
$UCPost->Velveteen_discolored = $request->input('Velveteen_discolored');
$UCPost->Rental = $request->input('Rental');
$UCPost->Rentals_Out = $request->input('Rentals_Out');
$UCPost->Rentals_Left = $request->input('Rentals_Left');
return $UCPost->where('id', $id)->update(
[
'gown_2019' => $UCPost->gown_2019,
'gown_2017_2018' => $UCPost->gown_2017_2018,
'gown_2016' => $UCPost->gown_2016,
'gown_2015' => $UCPost->gown_2015,
'Light_Blue' => $UCPost->Light_Blue,
'Seconds' => $UCPost->Seconds,
'Velveteen' => $UCPost->Velveteen,
'Velveteen_discolored' => $UCPost->Velveteen_discolored,
'Rental' => $UCPost->Rental ,
'Rentals_Out' => $UCPost->Rentals_Out,
'Rentals_Left' => $UCPost->Rentals_Left
]
);
}
The code above as I mentioned earlier, it always returns 1 regardless of any changes made to the form. I want it to return 0 if there are no changes by the user or if they accidentally hit the update button. I'm trying to find the equivalent of mysqli_affected_rows in Eloquent.
You are calling the update() method on your model. This will return 1 if the update was successful. It doesn't matter if there were any changes made, always 1 on successful update.
If you would like to only save to the database if there are changes, you can use the save() method instead, which checks to see if changes are present, and only writes to the database if the data is different. You have already created code to do this, by setting the model to have all of the new data from the sheet, so a simple save() at the end (with a check to see if it saves), will do what you want.
However, your current code is doing a lot of extra work. You are assigning all the variables to the model, and then updating based on that assignment of data to the model. You don't have to do all that assignment again in the update method. Once you have set the fields, you can save immediately, you don't have to re-assign all the variables. So:
$UCPost->gown_2019 = $request->input('Gown2019');
// etc..
$UCPost->Rentals_Left = $request->input('Rentals_Left');
$UCPost->save();
Will get you where you want to go and will only save if different.
If you have control over your form, and can change the form element names to match your database, this can be even easier. You can do all of this in one line:
$UCPost->update($request->all());
If you want to check if the model is dirty just call isDirty():
if($UCPost->isDirty()){
// changes have been made
}
Finally, if you want to verify if anything was changed after either method (save or update):
if ($UCPost->wasChanged()) {
// changes have been made
}
Hope this helps
Given following code:
class Picture {
public function getAbsolutePathAttribute() {
return "absolute_path"
}
}
$picture = new Picture();
echo $picture->absolute_path; // prints "absolute_path"
$picture->absolute_path = "I will override you no matter what";
echo $picture->absolute_path; // prints "absolute_path"
Is there way of overriding an eloquent mutator attribute?
I have tried a magic method:
setAbsolutePathAttribute($value) {
$this["attributes"] = $value;
}
However it did not work.
So I don't recommend trying to solve this by overriding the mutator. You might be able to, but the less you touch the core of Laravel the better. Why? Because you never know what future Laravel code could look like.
A core change could possibly break your internal override the next time you do a minor or major Laravel version upgrade.
So, 2 ways to solve this, given your simple example:
1. Create a default value at the database level
Laravel migrations feature a default column modifier, it looks something like this: ->default('something')
That way you don't need the mutator at all for getting that default value you're looking for.
You can read more about them here: https://laravel.com/docs/5.6/migrations#column-modifiers
2. Make your accessor smarter
Your example is pretty simple, so I'll just work with it:
class Picture {
public function getAbsolutePathAttribute() {
if(is_null($this->absolute_path)) {
return "absolute_path";
}
return $this->absolute_path;
}
}
That way it only does something if there is no value present.
I'm querying big chunks of data with cachephp's find. I use recursive 2. (I really need that much recursion sadly.) I want to cache the result from associations, but I don't know where to return them. For example I have a Card table and card belongs to Artist. When I query something from Card, the find method runs in the Card table, but not in the Artist table, but I get the Artist value for the Card's artist_id field and I see a query in the query log like this:
`Artist`.`id`, `Artist`.`name` FROM `swords`.`artists` AS `Artist` WHERE `Artist`.`id` = 93
My question is how can I cache this type of queries?
Thanks!
1. Where does Cake "do" this?
CakePHP does this really cool but - as you have discovered yourself - sometimes expensive operation in its different DataSource::read() Method implementations. For example in the Dbo Datasources its here. As you can see, you have no direct 'hooks' (= callbacks) at the point where Cake determines the value of the $recursive option and may decides to query your associations. BUT we have before and after callbacks.
2. Where to Cache the associated Data?
Such an operation is in my opinion best suited in the beforeFind and afterFind callback method of your Model classes OR equivalent with Model.beforeFind and Model.afterFind event listeners attached to the models event manager.
The general idea is to check your Cache in the beforeFind method. If you have some data cached, change the $recursive option to a lower value (e.g. -1, 0 or 1) and do the normal query. In the afterFind method, you merge your cached data with the newly fetched data from your database.
Note that beforeFind is only called on the Model from which you are actually fetching the data, whereas afterFind is also called on every associated Model, thus the $primary parameter.
3. An Example?
// We are in a Model.
protected $cacheKey;
public function beforeFind($query) {
if (isset($query["recursive"]) && $query["recursive"] == 2) {
$this->cacheKey = genereate_my_unique_query_cache_key($query); // Todo
if (Cache::read($this->cacheKey) !== false) {
$query["recursive"] = 0; // 1, -1, ...
return $query;
}
}
return parent::beforeFind($query);
}
public function afterFind($results, $primary = false) {
if ($primary && $this->cacheKey) {
if (($cachedData = Cache::read($this->cacheKey)) !== false) {
$results = array_merge($results, $cachedData);
// Maybe use Hash::merge() instead of array_merge
// or something completely different.
} else {
$data = ...;
// Extract your data from $results here,
// Hash::extract() is your friend!
// But use debug($results) if you have no clue :)
Cache::write($this->cacheKey, $data);
}
$this->cacheKey = null;
}
return parent::afterFind($results, $primary);
}
4. What else?
If you are having trouble with deep / high values of $recursion, have a look into Cake's Containable Behavior. This allows you to filter even the deepest recursions.
As another tip: sometimes such deep recursions can be a sign of a general bad or suboptimal design (Database Schema, general Software Architecture, Process and Functional flow of the Appliaction, and so on). Maybe there is an easier way to achieve your desired result?
The easiest way to do this is to install the CakePHP Autocache Plugin.
I've been using this (with several custom modifications) for the last 6 months, and it works extremely well. It will not only cache the recursive data as you want, but also any other model query. It can bring the number of queries per request to zero, and still be able to invalidate its cache when the data changes. It's the holy grail of caching... ad-hoc solutions aren't anywhere near as good as this plugin.
Write query result like following
Cache::write("cache_name",$result);
When you want to retrieve data from cache then write like
$results = Cache::read("cache_name");
So maybe I am just missing something. The SaveAll data works fine... if I take out the beforeSave or just return true no matter what everything is saved. What I am trying to do is check if the combination of user_group_id/user_friend_id allready exists. If it does don't save.
However it seems if you return false for one record the entire saveAll function quits running. Maybe this is intentional? The offical Doc is abit limited to answer this.
Obviously I could move the check to a function but this seemed better to cover every insert/update.
function beforeSave($options) {
//dont let any duplicates be saved
$count = $this->find('count', array(
'conditions' => array(
'user_group_id' => $this->data['UserGroupFriend']['user_group_id'],
'user_friend_id' => $this->data['UserGroupFriend']['user_friend_id'],
)));
if ($count)
return false;
else
return true;
}
In beforeSave() you can check $this->data for invalid values and if found, modify $this->data to remove them. Then return true and only the data still present in $this->data will be saved.
A less invasive (and perhaps more predictable) way of doing this is to let the framework handle it for you. Instead of hacking together validation manually, just let Cake do it for you. Create a custom validation rule on your UserFriendGroup to validate the uniqueness of these 2 fields and then the saveAll() method just works as usual. This is just something else to validate during it's normal validation execution.
I've linked to the custom validation rule for Cake 1.3 since you didn't specify a version, but I'm sure 2.x has something very similar.