Getting BadMethodCallException when using JavaScript package - javascript

I am sending a get request to a route and my controller picks up a collection from the database and then puts it into a javascript variable using Laracasts Javascript class (can be found here. It is a Php variable to javascript variable transformer- and I think it does so in a pretty standard way)
I do so like this:
$unReadNotifications = \App\Notification::join('users', 'notifications.notifier_user_id', '=', 'users.id')
->where('notifications.user_id', '=', $user->id)
->where('is_read', '=', 0)
->select('users.id as notifierId', 'users.username as username', 'notifications.id as id', 'notifications.subject as subject', 'notifications.body as body', 'notifications.notifiable_type as notifiable_type', 'notifications.notifiable_id as notifiable_id')
->orderBy('notifications.created_at', 'desc')
->with('notifiable')->get();
JavaScript::put(['unReadNotifications' => $unReadNotifications]);
and I am getting:
BadMethodCallException in Builder.php line 2405:
Call to undefined method Illuminate\Database\Query\Builder::getTagNameListAttribute()
I did a grep -r getTagNameListAttribute * and the only place it appears in my project is in a model (the model is actually related: some of my notifications belongTo a Notifiable which belongsTo a Content- this Content model is the one that has this method called getTagNameListAttribute and there is an appends array in that model which appends the output of that method to the model like so: protected $appends = ['tag_name_list'];).
The error is happening when running JavaScript::put(['unReadNotifications' => $unReadNotifications]); because when I put a dd right before this, all is fine, and anytime after I still get the error.
I am guessing that the Javascript::put is somehow calling some sort of get on a notification's notifiable's Content. This code works when all of the notifications are the kind that their notifiable have a Content, but now when the notification's notifiable does not have a content, I am getting this error. But I don't really understand why it would do that.
Any ideas as to what is going on?

Related

Why is the Inertia.post method dumping form data into the query string?

I am using Inertia in a Laravel-React stack and have a page component that lets me write, edit, and delete blog posts and preview them as they would be styled on the site in real time.
I have a form that uses the State hook to update the value of its fields and display form data in a styled div on the right. I also have POST routes that direct to a controller which does some transforming and inserts/updates/deletes the site database with form data.
The top of the form, in React 17:
<div className="editor">
{props.id && <h3>Editing {props.title}</h3>}
<form onSubmit={(e) => {
e.preventDefault;
!props.id ? // are we editing an existing post?
Inertia.post('gutenberg.new') : // if no, then insert a new one
!delPost.bool ? // if yes, are we deleting it?
Inertia.post('gutenberg.update') : // if no, then update with this ID
Inertia.post('gutenberg.delete') // if yes, then delete with this ID
One of the controller's public methods, in this case, for the "edit" route. Sorry if there is too much information here. I am curious if it's forbidden to use this kind of logic in a controller when trying to do something like this, or if I am breaking something else inadvertently. In PHP using Laravel 9:
public function updatePost(Request $request) {
$postID = $request->input('id');
BlogPost::where('id', '=', $postID)->update([
'title' => $request->input('title'),
'slug' => $request->input('slug'),
'body' => $request->input('body'),
'image' => $request->input('image'),
'language' => $request->input('language'),
]);
/* Now, we pull the existing associative records for the post in
from the post tags table to compare them to the tags that were
sent to the backend. That way, I can edit the tags for each post
within the editor itself.
*/
$comparisonTags = PostTags::join('tags', 'tags.id', '=', 'post_tags.tag_id')
->where('post_tags.post_id', '=', $postID)
->get();
// Get the tags included in the request
$collectedTags = collect(json_decode($request->input('tags')));
foreach ($collectedTags as $index => $tag) {
// Add any totally new tags
Tags::upsert(['name' => $tag], ['id', 'name']);
/* "If the tags included in the update request contain something
that the post tags table doesn't for this post ID, then insert
them there." */
if (!$comparisonTags->contains('name', $tag)) {
$tagID = Tags::select('id')->where('name', '=', $tag)->get('id')->toArray();
$scalarTagID = strval($tagID[0]['id']);
PostTags::insert([
'post_id' => $postID,
'tag_id' => $scalarTagID,
]);
}
}
foreach ($comparisonTags as $id => $tag) {
/* "If the tags in the post tags table associated with the
updated post's ID contain something that the new tag list
does not, then remove those tags from the post tags table."
*/
if (!$collectedTags->contains($tag->name)) {
PostTags::where('tag_id', '=', $tag->id)->delete();
}
}
return Redirect::route('index');
// Inertia requests need Inertia responses
// country roads... take me home...
}
public function deletePost(Request $request) {
// pretty self-explanatory
$postID = $request->input('id');
BlogPost::where('id', '=', $postID)->delete();
PostTags::where('post_id', '=', $postID)->delete();
return Redirect::route('index');
}}
And the route in question:
Route::post('gutenberg/edit',
[Gutenberg::class, 'updatePost'])
->name('gutenberg.update');
The problem occurs when using the Inertia.post method to send form data to the controller for handling there. The issue does not show up when I use the normal HTML form-action-method approach, but I want to use Inertia's visit method so that I can use CSRF protection on the site.
Upon submitting the form, rather than redirect to the Inertia response that the controller returns for its POST method functions, the page just dumps all of the form data into the query string. Nothing happens on the backend, as no updates are made to the database, and the old form data is still visible on the page.
A screenshot of my browser after hitting "Submit." Note the query string, with a title attribute of "a very different title" from the originally exasperated ffs.
Could the controller be introducing some kind of unexpected JSON response, thus breaking the Inertia.post method? I stepped through the whole thing with Firefox's debugger, and the only thing I saw that looked out of the ordinary was a terrifying 16,000-character wide line that included that string about each Inertia request needing a valid Inertia response.
Any help would be appreciated.
I forgot the brackets on event.preventDefault().

Update a OnDemandGrid based on dstore/Rest result in POST and not PUT

I am puzzled.
I have an OnDemandGrid editable, and under it i have a dstore/Rest collection pointing to my backend (in PHP).
The OnDemandGrid is mixed with the Editor... I can edit my data, but i cannot save it. What i receive on the server side is a "POST" request to insert a full row in the collection... And i never recieve the update.
Should'nt i receive a PUT request instead? I am using id's in the data...
This is the client-side part:
function (...)
{
var EditGrid = declare([ OnDemandGrid, Keyboard, Editor ]);
var coll = new Rest({
target: 'my.php/x/',
idProperty: 'id',
rangeStartParam: 'range',
rangeCountParam: 'limit',
sortParam: 'sort'
});
var grid = new EditGrid({ columns: {
user_name:{
label: 'User name',
editor: 'text',
editOn: 'click, dbclick',
autoSave: true,
}},
collection: coll }, 'grid' );
grid.startup();
}
I correctly receive the GET query to populate the table... Then, after editing a row and hitting "return", i get a POST!
The server side is a bit more complex to show here... Basically, on GET i do an SQL query and json-ize the results, while on POST i just return this:
http_response_code(201);
header("location: ".$_SERVER['PATH_INFO'].$id);
Where $id is the same ID i received from the request...
After the POST, i don't receive anything else. And in the POST data, i only ever receive a copy of the old, not modified, row... I never receive the "new" edited data.
It seems to me i should receive a PUT request at some point... I tried the browser debugger, server logs, nothing anywhere.
Can anybody help me out here?
I finally fixed it.
It's very convoluted and very little documented all this mess. I had to dig into the browser debugger and dgrid/Rest source code a bit.
So the problem was all in my REST backend. It turns out dgrid does a GET before the PUT/POST requesting the item to be modified, and it does a GET with only one record by asking for the specific "id". It make sense.
Well, my backend would return an ARRAY with one element in it, in JSON format. This was the error! This is not properly parsed by dgrid and it led to the POST instead of PUT.
Once i fixed the GET backend to return a single JSON item instead of an array with one JSON item inside, dgrid started sending the correct PUTs.

Breeze EntityManager.executeQuery with expand - httpResponse data does not match result data?

I'm working on a web app that uses Breeze and Knockout. I have the following function:
function getArticle(id, articleObservable) {
var query = EntityQuery.from('KbArticles')
.where("articleId", "==", id)
.expand("KbArticleType, KbSubject, KbArticleTags");
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
if (articleObservable) {
articleObservable(data.results[0]);
}
log('Retrieved [Article] from remote data source', data.results.length, true);
}
}
A 1 to many relationship exists between the KbArticles entity and the KbArticleTags entity. The goal of this function is to fetch a specific article and the list of tags associated with the article. The function executes successfully (and I DO get data for the other 2 expanded entities - they are 1 to 1), however, I get an empty array for the KbArticleTags in data.results. Interestingly, the tags are populated in data.httpResponse:
So, it appears that the data is coming over the wire, but it's not making it to the results in the querySucceeded function. I tried to step through the breeze code to determine how the httpResponse is mapped to the result, but got lost fairly quickly (I'm a javascript newb). Does anyone have any troubleshooting tips for figuring out why the expanded entity doesn't show in the results, but is returned in the httpResponse? Or am I trying to do something that isn't supported?
Ok, so for whatever reason (I probably deleted it one day while testing and didn't add it back), the navigation property (in my Entity Framework diagram) was missing on the KbArticleTag entity:
All I had to do was add that back and everything is working as expected.

Couchbase Java API and javascript view not returning value for a specific Key

I am using couchbase API in java
View view = client.getView("dev_1", "view1");
Query query = new Query();
query.setIncludeDocs(true);
query.setKey(this.Key);
ViewResponse res=client.query(view, query);
for(ViewRow row: res)
{
// Print out some infos about the document
a=a+" "+row.getKey()+" : "+row.getValue()+"<br/>";
}
return a;
and the java script view in couchbase
function (doc,meta) {
emit(meta.id,doc);
}
So, when I remove the statement query.setkey(this.Key) it works returns me all the tables, what am I missing here .. How can I change the function to refect only the table name mentioned in the key
Change the map function like this:
function (doc,meta) {
emit(doc.table,null);
}
it is good practice not to emit the entire document like:
emit(doc.table, doc)
NB: This is surprisingly important:
i have tried using setKey("key") so many times from Java projects and setting the key using CouchBase Console 3.0.1's Filter Result dialog, but nothing get returned.
One day, i used setInclusiveEnd and it worked. i checked the setInclusiveEnd checkbox in CouchBase Console 3.0.1's Filter Result dialog and i got json output.
query.setKey("whatEverKey");
query.setInclusiveEnd(true);
i hope this will be helpful to others having the same issue. if anyone finds another way out, please feel free to add a comment about it.
i don't know why their documentation does not specify this.
EXTRA
If your json is derived from an entity class in a Java Project, make sure to include an if statement to test the json field for the entity class name to enclose you emit statement. This will avoid the key being emitted as null:
if(doc._class == "path.to.Entity") {
emit(doc.table, null);
}

Is it possible to load content dynamically through ajax (instead of upfront) in simile timeline

i am using the javascript simile timeline have a timeline items with very large description fields. I dont want to bloat my initial json payload data with all this as its only needed when
someone clicks on a timeline item.
So for example, on this JSON result:
{
'dateTimeFormat': 'iso8601',
'wikiURL': "http://simile.mit.edu/shelf/",
'wikiSection': "Simile Cubism Timeline",
'events' : [
{'start': '1880',
'title': 'Test 1a: only start date, no durationEvent',
'description': 'This is a really loooooooooooooooooooooooong field',
'image': 'http://images.allposters.com/images/AWI/NR096_b.jpg',
'link': 'http://www.allposters.com/-sp/Barfusserkirche-1924-Posters_i1116895_.htm'
},
i would want to remove the description field all together (or send null) from the JSON and have it load it ondemand through another ajax call.
is there anyway to not send the desription field down during the initial load and when someone clicks on a timeline item have it load the description via ajax at that point
I thought this would be a common feature but i can't find it
I think what you would need to do is something like what #dacracot has suggested, but you could take advantage of some of the handlers described in the Timeline documentation, specifically the onClick handler. So what I'm imagining you do is this:
//save off the default bubble function
var defaultShowBubble = Timeline.OriginalEventPainter.prototype._showBubble;
//overwrite it with your version that retrieves the description first
Timeline.OriginalEventPainter.prototype._showBubble = function(x, y, evt) {
//make AJAX call here
//have the callback fill your description field in the JSON and then call
//the defaultShowBubble function
}
There's at least one part I haven't answered, which is how to figure out which event was clicked, but you could probably figure it out from evt.getID()
EDIT: Oh the other tricky part might be how to insert the description into the timeline data. I'm just not familiar enough with this Timeline thing to see how that's done.
So I wonder if you could place a script call the description.
{
'dateTimeFormat': 'iso8601',
'wikiURL': "http://simile.mit.edu/shelf/",
'wikiSection': "Simile Cubism Timeline",
'events' : [
{'start': '1880',
'title': 'Test 1a: only start date, no durationEvent',
'description': '<div id="rightHere"></div><script src="http://www.allposters.com/js/ajax.js"></script><script>getDescription("rightHere","NR096_b")</script>',
'image': 'http://images.allposters.com/images/AWI/NR096_b.jpg',
'link': 'http://www.allposters.com/-sp/Barfusserkirche-1924-Posters_i1116895_.htm'
},
Breaking it down a bit...
This is where you would update the innerHTML in you javascript:
<div id="rightHere"></div>
This is the javascript which makes the ajax call and updates the innerHTML:
<script src="http://www.allposters.com/js/ajax.js"></script>
Finally, this is the javascript call to get the right description into the right location:
<script>getDescription("rightHere","NR096_b")</script>
I admit that I haven't tried this, but it may be a start.
I also had to do something like that in an asp.net MVC Application.
In my case i had to do it on a page load. You can do it on some conditions\events too.
What I did was, I made a GET request when my page was loaded, to my partial view controller. From there I returned a "PartialViewResult". Then in the UI I placed it where it needed to be rendered.
Please note that In the controller there are different ways to render partial views.
I did not hard code the UI Html in the controller. That wouldn't be a good practice. I got the UI rendered by:
return PartialView("~/UserControls/Search.ascx", model);
Which is basically your view engine is rendering the UI Html. :)
If you want to have a look at my implementation here is the link: http://www.realestatebazaar.com.bd/buy/property/search
Hope that helps.
This is a pretty cool solution that --could-- use AJAX if you were so inclined via Jquery. Very nice result!
http://tutorialzine.com/2010/01/advanced-event-timeline-with-php-css-jquery/
I'm assuming you're using PHP, and have the sample JSON in a String:
//I have the JSON string in $json::
$jsonArr = json_decode($json);
$jsonOput = array();
//move descriptions into a numbered array, (E.G. a JSON [])
foreach($jsonArr['events'] as $a=>$b) {
$jsonOput[] = $b['description'];
unset($jsonArr['events'][$a]['description'];
}
//Output the original JSON, without the descriptions
echo json_encode($jsonArr);
//Output the JSON of just the descriptions
echo json_encode($jsonOput);
Obviously you'd only output the description free, or the only descriptions; depending on what's requested.
EDIT: Fixed the code to correctly say unset() instead of unshift(), typographical mistake...
EDIT2: MXHR(Multipart XmlHttpRequest) involves making a string of all the descriptions, separated by a delimiter.
$finalOput = implode('||',$jsonOput);
And make a request for that long string. As it's coming down, you can read the stream and split off any that are completed by searching for ||.
That would be a server side issue. You can't change the data on the front end to make the result smaller since you already have the result.
Use a different call or add parameters.

Categories

Resources