DateTime Issue with Laravel 5.2 & MS SQL Server - sql-server

Found this, but it didn't help: https://laracasts.com/discuss/channels/eloquent/timestampdatetime-issue-with-51-sqlserver
When I set my date field on the model like so protected $dates = ['OrderDate','TimeStamp']; then call $order->OrderDate I get the following error:
InvalidArgumentException with message 'Unexpected data found.
Unexpected data found.
The separation symbol could not be found
The format separator does not match
Trailing data'
But, if I manually create a Carbon date object using the same formatting and copying\pasting the date directly from SQL Server, it does so successfully:
>>> Carbon\Carbon::createFromFormat('Y-m-d H:i:s.000', '2015-12-29 00:00:00.000');
=> Carbon\Carbon {#835
+"date": "2015-12-29 00:00:00.000000",
+"timezone_type": 3,
+"timezone": "UTC",
}
What am I missing?

For some reason Laravel was seeing it as M j Y h:i:s:000A -- probably the driver. I added protected $dateFormat = 'M j Y h:i:s:000A'; to all the MS SQL Server models I am using.

The workaround would be to setup accessors and mutators and parse date manually, instead of putting it in $dates array.
Something like that:
public function getOrderDateAttribute($value)
{
return Carbon::createFromFormat('Y-m-d H:i:s.000', $value);
}
public function getTimeStampAttribute($value)
{
return Carbon::createFromFormat('Y-m-d H:i:s.000', $value);
}
public function setOrderDateAttribute($value)
{
$this->attributes['OrderDate'] = Carbon::createFromFormat('Y-m-d H:i:s', $value)->format('Y-m-d H:i:s.000');
}
public function setTimeStampAttribute($value)
{
$this->attributes['TimeStamp'] = Carbon::createFromFormat('Y-m-d H:i:s.000', $value)->format('Y-m-d H:i:s.000');
}

Related

Laravel - Scope - Error converting data type nvarchar to numeric

I have a model called "Consol", where I have a scope that looks like this:
public function scopeOfFinalsSent($query, $set)
{
if ($set) {
return $query->whereNotNull('finals_sent')->where(function ($query) {
$query->where('final_weight', '>=', 'current_weight')
->where('final_cbm', '>=', 'current_cbm');
});
}
return $query;
}
I am using it like this:
return Consol::ofFinalsSent(true)->count();
When using this scope together with MSSQL, I get the below error:
SQLSTATE[42000]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Error converting data type nvarchar to numeric. (SQL: select count(*) as aggregate from [consols] where [first_etd] between 2020-04-20 and 2020-04-26 and [finals_sent] is not null and ([final_weight] >= current_weight and [final_cbm] >= current_cbm))
Please note, if I switch over to MySQL, I don't get the above error.
I have tried to debug the actual query, by using the toSql() method, on my scope. The query, that I am performing looks like this:
select * from [consols] where [first_etd] between '2020-04-20' and '2020-04-26' and [finals_sent] is not null and ([final_weight] >= "current_weight" and [final_cbm] >= "current_cbm")
When running this query directly in the SQL Server Management Studio on the actual table, the query is being executed perfectly.
Below you can see the schema for the table:
You need to use whereColumn to compare values of columns, in your case:
public function scopeOfFinalsSent($query, $set)
{
if ($set) {
return $query->whereNotNull('finals_sent')->where(function ($query) {
$query->whereColumn('final_weight', '>=', 'current_weight')
->whereColumn('final_cbm', '>=', 'current_cbm');
});
}
return $query;
}
Otherwise you end up comparing things like final_weight to the string value "current_weight"
I fixed this issue by using the whereRaw method that Laravel provides, as I suspected something "going on behind the scenes" with the where method.
My scope now looks like this (notice the whereRaw instead of where being used):
public function scopeOfFinalsSent($query, $set)
{
if ($set) {
return $query->whereNotNull('finals_sent')->where(function ($query) {
$query->whereRaw('final_weight >= current_weight')
->whereRaw('final_cbm >= current_cbm');
});
}
return $query;
}

Laravel limit by ID

I have the code below to get all of my messages from my database. All messages contain a room_ID. I want to have the most recent 20 messages of each room when I go to localhost:8000/api/messages. Is it possible and how?
public function showAll()
{
$messages = Message::all();
foreach ($messages as $message) {
$message->user;
$message->room;
}
return response()->json($messages);
}
What you need is this:
https://laravel.com/docs/5.7/pagination
Flat and simple, you would just do this for each of the rooms:
$messages = Message::where('room_id', $room->id)->orderBy('date_column')
->paginate(20)->get();
This ofcourse can be simplified if you setup your relations properly.

Indirect modification of overloaded element of Illuminate\Support\Collection has no effect

im quite new in laravel framework, and im from codeigniter.
I would like to add new key and value from database
static function m_get_promotion_banner(){
$query = DB::table("promotion_banner")
->select('promotion_banner_id','promotion_link','about_promotion')
->where('promotion_active','1')
->get();
if($query != null){
foreach ($query as $key => $row){
$query[$key]['promotion_image'] = URL::to('home/image/banner/'.$row['promotion_banner_id']);
}
}
return $query;
}
that code was just changed from codeigniter to laravel, since in codeigniter there are no problem in passing a new key and value in foreach statement
but when i tried it in laravel i got this following error :
Indirect modification of overloaded element of Illuminate\Support\Collection has no effect
at HandleExceptions->handleError(8, 'Indirect modification of overloaded element of Illuminate\Support\Collection has no effect', 'C:\xampp\htdocs\laravel-site\application\app\models\main\Main_home_m.php', 653, array('query' => object(Collection), 'row' => array('promotion_banner_id' => 1, 'promotion_link' => 'http://localhost/deal/home/voucher', 'about_promotion' => ''), 'key' => 0))
please guide me how to fix this
thank you (:
The result of a Laravel query will always be a Collection. To add a property to all the objects in this collection, you can use the map function.
$query = $query->map(function ($object) {
// Add the new property
$object->promotion_image = URL::to('home/image/banner/' . $object->promotion_banner_id);
// Return the new object
return $object;
});
Also, you can get and set the properties using actual object properties and not array keys. This makes the code much more readable in my opinion.
For others who needs a solution you can use jsonserialize method to modify the collection.
Such as:
$data = $data->jsonserialize();
//do your changes here now.
The problem is the get is returning a collection of stdObject
Instead of adding the new field to the result of your query, modify the model of what you are returning.
So, assuming you have a PromotionBanner.php model file in your app directory, edit it and then add these 2 blocks of code:
protected $appends = array('promotionImage');
here you just added the custom field. Now you tell the model how to fill it:
public function getPromotionImageAttribute() {
return (url('home/image/banner/'.$this->promotion_banner_id));
}
Now, you get your banners through your model:
static function m_get_promotion_banner(){
return \App\PromotionBanner::where('promotion_active','1')->get();
}
Now you can access your promotionImage propierty in your result
P.D:
In the case you are NOT using a model... Well, just create the file app\PromotionImage.php:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PromotionImage extends Model
{
protected $appends = array('imageAttribute');
protected $table = 'promotion_banner';
public function getPromotionImageAttribute() {
return (url('home/image/banner/'.$this->promotion_banner_id));
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'promotion_banner_id','promotion_link','about_promotion','promotion_active'
];
just improving, in case you need to pass data inside the query
$url = 'home/image/banner/';
$query = $query->map(function ($object) use ($url) {
// Add the new property
$object->promotion_image = URL::to( $url . $object->promotion_banner_id);
// Return the new object
return $object;
});
I've been struggling with this all evening, and I'm still not sure what my problem is.
I've used ->get() to actually execute the query, and I've tried by ->toArray() and ->jsonserialize() on the data and it didn't fix the problem.
In the end, the work-around I found was this:
$task = Tasks::where("user_id", $userId)->first()->toArray();
$task = json_decode(json_encode($task), true);
$task["foo"] = "bar";
Using json_encode and then json_decode on it again freed it up from whatever was keeping me from editing it.
That's a hacky work-around at best, but if anyone else just needs to push past this problem and get on with their work, this might solve the problem for you.

Grails get() generating incomplete SQL

I'm upgrading an app from Grails 1.3.6 to 2.1.1. Have hit what seems to be a very strange issue.
I have a table called SECTION. This call:
def testSection = Section.get(94725)
Generates this SQL in Grails 1.3.6:
select
section0_.ID as ID42_0_,
section0_.concept_tag_uri as concept2_42_0_,
section0_.INDEX_ID as INDEX3_42_0_,
section0_.LIVE_IND as LIVE4_42_0_,
section0_.NAME as NAME42_0_,
section0_.PARENT_ID as PARENT6_42_0_,
section0_.ASST_INDEX_ASSET_ID as ASST7_42_0_,
section0_.ORDER_NO as ORDER8_42_0_,
section0_.SITE_ID as SITE9_42_0_,
section0_.TYPE_ID as TYPE10_42_0_,
(SELECT
uk_sec.id
FROM
section uk_sec,
site uk_site
WHERE
uk_sec.index_id = section0_.index_id
AND uk_sec.site_id = uk_site.siteid
AND uk_site.audienceid = 1) as formula1_0_
from
SECTION section0_
where
section0_.ID=?
But this SQL in 2.1.1:
select
section0_.ID as ID45_0_,
section0_.concept_tag_uri as concept2_45_0_,
section0_.INDEX_ID as INDEX3_45_0_,
section0_.LIVE_IND as LIVE4_45_0_,
section0_.NAME as NAME45_0_,
section0_.PARENT_ID as PARENT6_45_0_,
section0_.ASST_INDEX_ASSET_ID as ASST7_45_0_,
section0_.ORDER_NO as ORDER8_45_0_,
section0_.SITE_ID as SITE9_45_0_,
(SELECT
uk_sec.id
FROM section uk_sec,
site uk_site
WHERE uk_sec.index_id = section0_.index_id
AND uk_sec.site_id = uk_site.siteid
AND uk_site.audienceid = 1) as formula1_0_
from
SECTION section0_ where section0_.ID=?
Looks similar, but this part is missing in the 2.1.1 version:
section0_.TYPE_ID as TYPE10_42_0_,
This is the mapping in the class:
...
String sectionType
...
static mapping =
{
table 'SECTION'
version false
id generator:'assigned'
columns {
...
sectionType column: 'TYPE_ID'
...
}
}
The DB field in question is: TYPE_ID CHAR(1) but as the SQL is generated as a result of the get(), am assuming it's nothing to do with the actual DB schema or column(?).
The fact that it's the final element in the list of select fields makes me think it's somehow related, but I don't know how to change that, or if doing so would just 'delete' another field from the statement.
Anyone seen anything like this?
Cheers,
spacebot
Maybe somewhat hasty to think it might be a bug.
The parameter type of the overridden setter is required for Grails2.1.1, it's more forgiving in 1.x.
public void setType(type) { ... } // works in 1.3.6, breaks 2.1.1
public void setType(String type) { ... } // ok in 2.1.1

RIA-Services - how to WhereOr or use an IN style construct

I am using SL 4, WCF RIA Services against Entity Framework 4.0. I have an Entity, Visit, that has a string Status field. I have a search screen where I need to display results that have StatusA or StatusB. I am struggling to find a way to specify a client-side query that specifies a collection of statuses that should be matched. If I was to write what I want in SQL it would look something like:
select * from Visit where Status in ('StatusA', 'StatusB');
Client side, it appears to be straightforward to chain Where methods for a WhereAnd effect:
var query = this.PqContext.GetVisitsQuery();
if (!string.IsNullOrEmpty(this.PracticeName))
{
query = query.Where(v => v.PracticeName.ToUpper().Contains(this.PracticeName.ToUpper()));
}
if (this.VisitDateAfter.HasValue)
{
query = query.Where(v => v.VisitDate > this.VisitDateAfter);
}
if (this.VisitDateBefore.HasValue)
{
query = query.Where(v => v.VisitDate < this.VisitDateBefore);
}
However, I can't seem to find a straightforward way to do a WhereOr style operation. I have tried this:
var statuses = new List<string>();
if (this.ShowStatusA)
{
statuses.Add("StatusA");
}
if (this.ShowStatusB)
{
statuses.Add("StatusB");
}
if (statuses.Any())
{
query = query.Where(BuildContainsExpression<Visit, string>(e => e.Status, statuses));
}
Where BuildContainsExpression looks like:
private static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values)
{
throw new ArgumentNullException("values");
}
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any())
{
return e => false;
}
var equals =
values.Select(
value =>
(Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate<Expression>(Expression.Or);
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
But this throws a "Bitwise operators are not supported in queries." exception. Any clues? Is there an alternative way to build an expression tree that works here or do I need to pass all the parameters over to the server and use the BuildContainsExpression there?
Your time and your guidance are much appreciated.
You can create a query method such as the following in your domain service:
GetVisitsByStatus(string[] statusList) {
// create the LINQ where clause here
}
And then from the client, call context.GetVistsByStatusQuery(string[]).
Not all of LINQ is (or even can) be exposed over the URL, so there are always cases where you need to use simple parameters, and have the middle tier construct the LINQ expressions that eventually define the query that goes to the back-end data store.
Hope that helps.

Resources