A great many of the tests use a pattern of getting a response from an endpoint, running $data = $response->decodeResponseJson()
, and then making assertions against the resulting PHP array in $data
. Unfortunately, in Laravel 8 the return type is now \Illuminate\Testing\AssertableJsonString
, and I kept running into errors such as:
Argument #2 of PHPUnit\Framework\Assert::assertArrayHasKey() must be an array or ArrayAccess
I made various attempts to convert the output of decodeResponseJson
into an array (and being confused as to why it didn’t work, since AssertableJsonString implements ArrayAccess
), but in the end the solution was simple.
Instead of refactoring every existing test to use Laravel’s new JSON-based assertions, I merely had to change each call to decodeResponseJson
to just json
. That is, switching to
$data = $response->json();
was all it took.
]]>In the initial version, the primary usefulness of the library lies in two parts. First is its knowledge of which three digits of a given MMSI represent a MID code, since the location of the Marine Identification Digits is not the same in every MMSI. Second is its ability to provide the ISO 3166 code for the jurisdiction,1 along with its name and (if different) full name.
For example, given the MMSI “316123456”, with mmsi.js you can find out that the MID code is “316”, which corresponds to a code of “CA” and a country of “Canada”. With a different format, “986681234” has a MID code of “668”, corresponding to São Tomé and Príncipe, with a code of “ST” and a full name of “São Tomé and Príncipe (Democratic Republic of)”.
Additional information encoded in MMSIs includes details about the type of transceiver to which the code is assigned, such as vessel, search-and-rescue aircraft, or shore station. I plan to add more functionality to extract these sorts of details, as well as to improve the detection of valid values beyond simply checking for nine numeric digits.
Today I released the initial version of a Vue2Leaflet plugin to provide a simple wrapper of the polyline measurement tool as a Vue2Leaflet plugin, via the vue2-leaflet-polyline-measure component. Usage is simple:
<l-map> <l-polyline-measure :options="{ showUnitControl: true }" position="bottomright"/> <!-- other map components --> </l-map>
The options object is passed directly to the underlying library, so can be any of the polyline measurement options. The position parameter places the control in a corner of the map based on the available Leaflet control options.
]]>// This will find posts from user 4 twice! const opts = { force: true }; store.findAll('post', { userId: 2 }, opts); store.findAll('post', { userId: 4 }, opts);
Here is a brief demonstration of the behaviour. In the fiddle two requests are made to the JSONPlaceholder test API, for posts from different users. When the responses are received, they both contain the posts for the user specified in the second query.
The simple solution is to not use the same object for multiple requests.
// This works as expected. store.findAll('post', { userId: 2 }, { force: true }); store.findAll('post', { userId: 4 }, { force: true });
Or more interestingly, why doesn’t the first method work?
Part of the process js-data-http uses is to move the query object passed as the second argument to findAll into a new property of the third argument, options.params. This is because the function that actually makes the network request expects any query parameters to be specified in options.params, at least in part because the query parameter format used in GET requests is not the same as the JSON-formatted query syntax used by js-data. So some amount of transformation is required between what we pass in to js-data and what it sends to the server.
However, because the transformation is done in place, js-data mutates the object that is passed into it. The chain of events goes roughly as follows, due to the asynchronous promise-based nature of the findAll operation.
This turned out to be a very simple solution to a problem that required a very lengthy debugging process, all because I made the incorrect assumption that a third-party library would not have undocumented side effects on the variables that I passed into it. It was complicated by the fact that the side effects were themselves dependent on a different parameter, and of course by the async nature of the operations, but at its heart that was the issue.
]]>For example, if you have a Post that hasMany Comments, and a component on your page that iterates over post.comments , then the comments list still won’t change in Vue when it does in js-data.
By adding a boolean option to specify that some js-data relations should be made reactive, and defining a VueReactiveRecord class that enables Vue reactivity on those relations, we can use related models in a Vue template and have them update when the data store changes. Check out this gist to see an example of the final result.
My first attempt was to hew as closely to Caleb’s example as possible, automatically discovering every relationship and making them all reactive. Unfortunately the resulted in neither side of the relationship working correctly. Even though a post and some associated comment records were in the store, post.comments was empty as was each comment.post . At first I wondered if there would be an infinite loop problem, of A reacting to B reacting to A reacting to B reacting … . Instead it appeared as if neither side was able to react even once, and the models were never connected to each other. Either way, I didn’t debug that issue and instead set up a method by which relationships would only become reactive if explicitly told to do so.
When defining the js-data mappers, I included a new optional property named vueReactive to the relationships that I explicitly wanted to make reactive within Vue components. For example, it’s much more likely that a new comment will be added to an existing post than that the post a comment was for will become a different one, so we can specify that the hasMany from posts to comments should be reactive, but leave the belongsTo from comments to posts alone:
defineMapper('post', ... ... relations: { hasMany: { comment: { foreignKey: 'post_id', localField: 'comments', vueReactive: true, }, }, }, );
With that in place, I updated Caleb’s ViewReactiveRecord class:
class ViewReactiveRecord extends Record { ... // Add Vue reactivity to relationships as well, when their definitions say to. const relationsByType = this._mapper().relations; // e.g. relationsByType = { hasMany: {...}, belongsTo: {...} } for (const relType in relationsByType) { const relations = relationsByType[relType]; // e.g. relations is all hasMany relationships, or all belongsTo ones. for (const relName in relations) { const relation = relations[relName]; // Now relation is the actual definition of a single relationship on the mapper if (!relation.vueReactive) { continue; } const key = relation.localField; Vue.util.defineReactive(this, key, this[key]); } } ... }
Now we can have a Vue template that does something such as this:
<div> <h1>{{ user.name }}</h1> <h2>Post titles</h2> <p v-for="post in user.posts" :key="post.id"> {{ post.title }} </p> </div>
and know that if the set of associated posts in the js-data store changes, the list of titles displayed will reactively update accordingly.
]]>
>>> $faker = Faker\Factory::create();
]]>
I was using js-data v3 with a schema containing an array field recently, and came across some initially baffling behaviour. I had a fooSchema that included a field definition along these lines:
import { Schema } from 'js-data'; const FooSchema = new Schema({ // ... barIds: { type: 'array', }, // ... });
A Foo object could have many associated Bar objects, and the API supplying the data specified them by including an array of their integer ids in the barIds field. Simple enough, and the above worked perfectly while consuming the output of the API. The unexpected behaviour didn’t start until I tried to send the array back to the API to update the record.
When calling the save method on a Record , the defined Mapper serializes it with the toJSON method. Given the above schema, a record with barIds === [2, 3, 5] was getting converted to the JSON field barIds: [{}, {}, {}]. The length of the JSON array was always equal to the length of the underlying data array, but it never contained anything except empty objects instead of the desired relationship information.
When an array field in a record with a schema is serialized by js-data, each element is mapped to an empty object unless a valid JSON Schema for the element is specified. This choice of defaulting to {} happens in the Schema#pick method, when the type property of the schema is 'array'. To prevent this, simply specify the expected schema for the array elements in the schema for the records. In my case since the ids are numbers, this meant:
import { Schema } from 'js-data'; const FooSchema = new Schema({ // ... barIds: { type: 'array', items: { type: 'number' }, }, // ... });
The items validation keyword specifies how the elements of an array-type property should be treated. If not specified, the default is to treat them as objects. If the array contains actual related elements, then a js-data schema could be given, for example bars: BarSchema. Otherwise, any valid JSON Schema definition should provide the desired results.
]]>By default, the addon creates a set of basic links with a configuration maximum number of pages to display at once, and arrows for navigating page by page and to the start and end of the list:
Each number except the current page is a link that will invoke an action, passing it the clicked value. Everything is classed for easy styling, and the icons to use for arrows and ellipses are configurable strings:
The documentation in the readme goes into more detail, and the issue tracker is open for business.
]]>{{#paper-select placeholder="Select a thing..." required=false allowClear=true ... }}
This option is passed along to the ember-power-select component that ember-paper uses under the hood, so the resulting clear button can be styled accordingly. For example,
.ember-power-select-clear-btn { position: absolute; right: 25px; }
Thanks to @miguelcobain for this tip on the community Slack channel.
]]>id
, which was previously the only option. (As of this writing the PR has been approved, but not yet merged.)
There is now a key called JWT_ID_FIELD
that can optionally be added to a project’s .env
file to specify the name of the field that should be used to look up and uniquely identify users. The default is of course id
, so any existing applications that don’t specify a value will continue to work without any changes.
So if the primary key field in your user table is user_id
, username
, or indeed anything else, then using lumen-jwt is straightforward:
# Specify accounts should be looked up by username JWT_ID_FIELD=username
My original use case for this feature was to allow using hashids to identify users without exposing database ids publicly. If an alternative unique id field such as this is persisted in your user model then you’re already done.
Another option is for the hashids to be computed properties, calculated via Eloquent mutators whenever a user instance is loaded. The down side to this approach is that there isn’t a straightforward method for retrieving the model corresponding to a given hashid. Instead, you can write a custom user provider to decode the hashid before retrieving the user―that will be the subject of a future post.
]]>