How to create a link to another resource in a datagrid? - admin-on-rest

In the tutorial (https://marmelab.com/admin-on-rest//Tutorial.html), you can see how to reference another entity (i.e post references a user)
This lead to the fact that another XHR is required to display the user name.
If my REST API returns something like
{
"id": 1,
"userId": 1,
"userName": "John Doe",
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
How can I directly display the userName property as a link to the user resource WITHOUT doing a XHR ?
Do I have to create something like InlinedReferenceField ?

Why don't you try:
import { Link } from 'react-router-dom';
const UserLinkField = ({ record }) => (
<Link to={`/users/${record.userId}`}>
{record.userName}
</Link>
);
Then use <UserLinkField> in the Datagrid?

Related

Str Limit for return of article body laravel

I have created an API for returning website articles :
public function showAllArticles(){
return Article::paginate(10);
}
in the article I have a text body (description) that may have a long text that I won't need, how can I put a str_limit to it ?
Default Output :
{
"current_page": 1,
"data": [
{
"id": 1,
"title": "Ut veniam dolorem et quia. Reprehenderit eum sed doloremque qui est. Sit rerum vel ex quia.",
"slug": "dignissimos-rerum-doloribus-necessitatibus-sit-autem-ad-fuga-cum",
"description": "Deleniti in soluta beatae id ipsa. Quia exercitationem est possimus reiciendis tempora odio. Et perspiciatis dolores dignissimos aperiam voluptatum.\n\nVoluptatem est in maiores porro error qui assumenda. Quaerat eaque vitae neque molestiae laboriosam quis necessitatibus. Vero ut qui sunt. Reiciendis dolorem enim eius recusandae praesentium repellendus.\n\nAd minima deleniti et placeat. Nisi veritatis neque et nobis voluptas. Doloremque officia aperiam aliquid repellendus nesciunt omnis.\n\nEt corrupti et totam est quod dolorum aliquam. Quo sit sequi distinctio inventore eveniet et ut. Ad voluptas perspiciatis incidunt sit. Voluptatem voluptas exercitationem debitis.\n\nEt hic beatae ducimus ad eligendi suscipit. Aut facilis magnam sequi id et ut. Reiciendis sequi iusto maiores nam delectus quasi.\n\nEt in officia earum dolores sunt et. Corrupti voluptatem delectus voluptates exercitationem molestiae aspernatur omnis corrupti. Consectetur sint quos vero nulla id alias. Possimus nam eius molestiae facere et non a.\n\nSint autem debitis corporis animi et beatae et. Aut occaecati nihil et temporibus perferendis. Commodi eligendi quo non et soluta. Vitae dolor sed qui distinctio numquam ad nihil.",
"user_id": 13,
"special": 0,
"image": null,
"created_at": "2021-10-27T21:36:37.000000Z",
"updated_at": "2021-10-27T21:36:37.000000Z"
}, ...
I want to make my "description" to have a lenth limit and add a extra "..." at the end of it.
I have tried this :
$data = Article::paginate(10);
$data->each(function($article){
// I can access each article with $article but I dont know how to modify it in $data
});
You can create an Eloquent: API Resource:
php artisan make:resource ArticleResource
And use the Str::limit helper for that property:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ArticleResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'description' => Str::limit($this->description, 50),
//...
];
}
}
Then in your controller return the resource collection:
use App\Http\Resources\ArticleResource;
use App\Models\User;
//...
public function showAllArticles(){
return ArticleResource::collection(Article::paginate(10));
}
You should use the collection function map instead of each, here you can find the documentation for the function: https://laravel.com/docs/8.x/collections#method-map
The map method iterates through the collection and passes each value to the given callback. The callback is free to modify the item and return it, thus forming a new collection of modified items
EDIT: There is another solution to your issue that would be better in regard of performance, and that is to make the shortening procedure in your query instead of handling it in PHP. Solution can be found here: https://laracasts.com/discuss/channels/laravel/how-to-get-trimmed-value-via-eloquent?page=1&replyId=435130
One solution would be with Custom Resources (like porloscerros already answeared) and one with Accesor. With accesors: You can add the method in your Article model:
use Illuminate\Support\Str;
public funtion getDescriptionAttribute($value)
{
$words = 20;
return Str::of($value)->words($words, ' >>>');
}

POST request with JSON server only inserting the Id, no content

server and normally I got it to work. However this time there is an boundary I want to save some time.
I have the following json file:
{ "posts": [
{
"id": 1,
"title": "React",
"category": "React-Development",
"imageUrl": "https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885_1280.jpg",
"text": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime mollitia, molestiae quas vel sint commodi repudiandae consequuntur voluptatum laborum numquam blanditiis harum quisquam eius sed odit fugiat iusto fuga praesentium optio, eaque rerum! Provident similique accusantium nemo autem. Veritatis obcaecati tenetur iure eius earum ut molestias architecto voluptate aliquam nihil, eveniet aliquid culpa officia aut! Impedit sit sunt quaerat, odit,tenetur error, harum nesciunt ipsum debitis quas aliquid. Reprehenderit, quia. Quo neque error repudiandae fuga? Ipsa laudantium molestias eos sapiente officiis modi at sunt excepturi expedita sint? Sed quibusdam recusandae alias error harum maxime adipisci amet laborum. Perspiciatis minima nesciunt dolorem! Officiis iure rerum voluptates a cumque velit ",
"date": "23/34/2021"
},
{
"id": 2,
"title": "React",
"category": "React-Development",
"imageUrl": "https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885_1280.jpg",
"text": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime mollitia, molestiae quas vel sint commodi repudiandae consequuntur voluptatum laborum numquam blanditiis harum quisquam eius sed odit fugiat iusto fuga praesentium optio, eaque rerum! Provident similique accusantium nemo autem. Veritatis obcaecati tenetur iure eius earum ut molestias architecto voluptate aliquam nihil, eveniet aliquid culpa officia aut! Impedit sit sunt quaerat, odit,tenetur error, harum nesciunt ipsum debitis quas aliquid. Reprehenderit, quia. Quo neque error repudiandae fuga? Ipsa laudantium molestias eos sapiente officiis modi at sunt excepturi expedita sint? Sed quibusdam recusandae alias error harum maxime adipisci amet laborum. Perspiciatis minima nesciunt dolorem! Officiis iure rerum voluptates a cumque velit ",
"date": "23/34/2021"
},
], "category": [
"React-Development",
"Vue3-Topics",
"Angular",
"Backend",
"Vue-Development",
"Angular-Development" ] }
I tried to post with and without Id. Should also work without Id I suppose.
{
"id": 99,
"title": "React",
"category": "React-Development",
"imageUrl": "https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885_1280.jpg",
"text": "Hello World",
"date": "23/34/2021"
}
However the result I got after a couple of tries.
{
"id": 9
}
Found it! Most likely you didn't change to JSON in the dropdown menu to the right of Body's options row.

Call a WebAPI in ASP.Net Core MVC

I have programmed a backend system in which I can manage data and then output it to an API. I did this because I want to convert a large project to a different language + framework, but I still want to integrate this backend system into the current version first, because it is much better than the old way. I have been looking for days how it works but I haven't really come to a result, I hope someone can help me.
example:
I have a separate page for each member with some data from a biography etc. and I can have this displayed in the WebAPI under: "https://sub.domain.de/items/member" then all members and associated data is output. Now I want that I to: "https://www.domain.de/member/[John_Doe]" (John_Doe is of course an example value for every other member)
can output certain data. the pages should of course still be generated dynamically.
ps: if it is necessary I can also enter "https://sub.domain.de/items/member/[John_Doe]" to only get the data from a specific member.
the json basically looks like this:
{
"data": [
{
"name": "John_Doe",
"status": "published",
"user_created": "052a2c25-b063-4a82-90cb-110d7f809cae",
"date_created": "2021-06-29T08:30:06+02:00",
"title": "Doc John Doe",
"Characteristics": "- **Lorem **: ipsum dolor sit amet\n- **Birthdate**: 1965",
"bio": "- **Lorem ipsum**:\nLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"member": true,
"section1": false,
"section2": false,
"portrait": null
},
{
"name": "Jane",
"status": "published",
"sort": null,
"user_created": "052a2c25-b063-4a82-90cb-110d7f809cae",
"date_created": "2021-06-29T08:29:31+02:00",
"title": "Jane",
"Characteristics": "",
"bio": "Bio unbekannt.",
"member": true,
"section1": false,
"section2": true,
"potrait": "30d17fe3-bcf7-4aa2-9759-49f3e257eb0f"
}
]
}
and the bio and the characteristics are written in markdown.
I hope I was able to explain what I mean and someone can help me.
edit:
ps: my main problem is that i don't know how to get data from an api, and of course i not only have the area with the members, i still have a lot of articles and more but it works with the same logic.
I guess this totally depending in the routing. you can construct your api that returned the data with this url :"https://sub.domain.de/items/member/[John_Doe]" like so
[Route("items/member")]
[ApiController]
public class UserController : ControllerBase
{
[HttpGet("{username}")]
public IActionResult GetUserData(string username) {
// your logic
}
}

Show relationship data in bulk

I am writing an API and there are my topics and comments related to it. My wish is to show all the topics while also showing the comments belonging to the topics. I established the relationship with the hasMany option, but I am having trouble showing the data.
Post Table
Comments Table
Post Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Posts extends Model
{
protected $table = 'posts';
protected $fillable = [
'post_id',
'user_id'
];
public function user(){
return $this->belongsTo('App\Models\Users');
}
public function comment()
{
return $this->hasMany('App\Models\Comments', 'post_id', 'id');
}
}
Comments Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Comments extends Model
{
protected $fillable = [
'user_id',
'post_id',
'description'
];
public function user()
{
return $this->belongsTo('App\Models\Users', 'user_id', 'id');
}
public function post()
{
return $this->belongsTo('App\Models\Posts', 'post_id', 'id');
}
}
For example, when I shoot all of the topics below, I want the comments with the user ids. How do I customize this?
{
"id": 1,
"user_id": 10,
"image": "https://lorempixel.com/800/400/cats/WhaleDevops/?43757",
"description": "Et quia enim distinctio non qui laudantium voluptatem. Cumque minus cum pariatur necessitatibus. Repellat qui provident voluptatum ut. Et sapiente eaque eum ut. Repudiandae eveniet a harum ea totam consectetur. Facere facilis sunt et consequuntur sapiente. A aspernatur placeat tenetur et. Omnis quasi sunt nostrum et velit sint quia. Velit temporibus ut aut ea in repudiandae.",
"created_at": "1999-04-11T17:37:42.000000Z",
"updated_at": "2020-08-08T10:41:28.000000Z"
},
{
"id": 2,
"user_id": 2,
"image": "https://lorempixel.com/800/400/cats/WhaleDevops/?53905",
"description": "Quasi ab recusandae molestiae pariatur et ut. Harum dolorum illo aspernatur fugit sequi aut. Modi quasi voluptas ad maxime ducimus quia molestiae maiores. Pariatur quam quam officia expedita. Alias quas aliquid et.",
"created_at": "1972-03-17T04:01:26.000000Z",
"updated_at": "2020-08-08T10:41:28.000000Z"
},
You will have to read up on Laravel Resources
When building an API, you may need a transformation layer that sits between your Eloquent models and the JSON responses that are actually returned to your application's users. Laravel's resource classes allow you to expressively and easily transform your models and model collections into JSON.
The documentation is well written and easy to follow. It should have everything you need.
You will need to read the Writing Resources chapter, that explains how to handle relationships.
You have to need make two changes.
1)Remove post_id from post table and place it in comment table.
2)Write the query
Post::with('comment','user')->get();

How to Implement React Redux Search and Checkbox Filter

I am trying to implement search/filter in my react project but can't seem to get it right.
Screenshot
This is what the user should be able to do:
Filter by text
Filter by location using the checkbox
or filter by job category
What I have right now is that I can filter by both text and checkbox, but I want the user to be able to search by text, e.g., "Dallas", "information technology", "sales" and then filter further by category or by location using the returned data from the text search.
This is my set up:
Searching by text uses a reducer to filter the array by search text
Checking a location or category checkbox stores the value in the category or keyword array
In the combine reducer file, I filter the jobs array in the store and memorize it using reselect.
I have a couple of glitches with the search text and checkbox functionalities. The filter by location checkbox functionality works fine by itself; however, when a checkbox is selected, and I enter search text, checkbox gets deselected. I want the checkbox to be still checked and displayed in the window when text is entered in the input field.
I'm a newbie, any help, suggestions, or a better approach to this functionality will be greatly appreciated.
SEARCHFORM.JS:
import React, {Component} from 'react';
//import {FormGroup, FormControl, InputGroup} from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { searchJobs, getJobs } from '../actions/jobaction';
import {selectKeywordCity,deselectKeywordCity} from '../actions/selectDeselectCityAction';
import {selectKeywordCategory,deselectKeywordCategory} from '../actions/selectDeselectCategoryAction';
class Search extends Component{
constructor(props){
super();
this.state = {
keyword:'',
checked:[]
};
this.handleChk = this.handleChk.bind(this);
}
handleInput = (event) => {
this.setState({
keyword: event.target.value
});
this.props.searchJobs(this.state.keyword);
}
/* handleSearch = (event) => {
event.preventDefault();
this.props.getJobs(this.state.keyword);
} */
clearForm = () => {
this.inputSearch.value = "";
this.setState({
keyword: this.inputSearch.value
});
this.props.getJobs();
}
handleChk = (e, keyword, type) => {
if(type==='city'){
e.target.checked ? this.props.selectKeywordCity(keyword) : this.props.deselectKeywordCity(keyword);
}else if(type==='category'){
e.target.checked ? this.props.selectKeywordCategory(keyword) : this.props.deselectKeywordCategory(keyword);
}
}
//Render Cities
renderCities(data){
if(data){
const cities = Object.keys(data).map(el => {
return data[el].city
}).sort()
.reduce((city,name) => {
const cityCount = city[name] ? city[name] + 1 : 1;
return{
...city, [name]:cityCount,
};
},{});
return Object.keys(cities).map((keyname, index) => {
return (
<li key={keyname}>
<input type="checkbox"
onChange={e=>this.handleChk(e,keyname,'city')}
className="chkbox"
/>
{keyname}
<span className="pull-right">{cities[keyname]}</span>
</li>)
})
}else{
return("nada")
}
}
//Render Job Categories
renderCategories(data){
if(data){
const categories = Object.keys(data).map(el => {
return data[el].category
}).sort()
.reduce((category,name) => {
const categoryCount = category[name] ? category[name] + 1 : 1;
return{
...category, [name]:categoryCount,
};
},{});
return Object.keys(categories).map((keyname, index) => {
return (
<li key={keyname}>
<input type="checkbox"
onChange={e=>this.handleChk(e,keyname,'category')}
className="chkbox"
/>
{keyname}
<span className="pull-right">{categories[keyname]}</span>
</li>)
})
}else{
return("nada")
}
}
render(){
let closeBtn = null;
if(this.state.keyword){
closeBtn = (<i className="fa fa-times" onClick={this.clearForm}></i>);
}
return(
<div>
<div className="side-search">
<i className="fa fa-search"></i>
<input type="text"
placeholder="Keyword Search"
ref={el => this.inputSearch = el}
onChange={this.handleInput}
value={this.state.keyword}
/>
{closeBtn}
</div>
<div className="chk_boxes">
<h4>Location</h4>
<ul>
{
this.renderCities(this.props.jobs)
}
</ul>
</div>
<div className="chk_boxes">
<h4>Category</h4>
<ul>
{
this.renderCategories(this.props.jobs)
}
</ul>
</div>
</div>
)
}
}
function mapStateToProps(state){
return{
jobs: state.jobs.jobs
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({searchJobs,
getJobs,
selectKeywordCity,
deselectKeywordCity,
selectKeywordCategory,
deselectKeywordCategory},
dispatch)
}
export default connect(mapStateToProps,mapDispatchToProps)(Search);
JOBACTION:
import axios from 'axios';
const FETCH_URL = 'http://localhost:3006';
/* GET ALL JOB LISTINGS */
export function getJobs(){
return function(dispatch){
axios.get(`${FETCH_URL}/jobs`)
.then(function(response){
dispatch({type:'GET_JOBS',payload:response.data})
}).catch(function(err){
dispatch({type:'GET_JOBS_ERROR', payload:err});
})
}
}
/* GET ALL JOB LISTINGS */
export function searchJobs(keyword){
return function(dispatch){
axios.get(`${FETCH_URL}/jobs?q=${keyword}`)
.then(function(response){
dispatch({type:'SEARCH_JOBS',payload:response.data})
}).catch(function(err){
dispatch({type:'SEARCH_JOBS_ERROR', payload:err});
})
}
}
JOBSLIST REDUCER:
export function Listings (state={}, action){
switch(action.type){
case 'GET_JOBS':
return{...state, jobs:action.payload}
case 'SEARCH_JOBS':
return{...state, jobs:action.payload}
default:
return state;
}
}
SELECTDESELECT(CHECKBOX) REDUCER:
const INITIAL_STATE = {
keywords:[]
}
export function filterCity(state=INITIAL_STATE, action){
switch(action.type){
case 'SELECT_CITY':
return{
keywords:[...state.keywords, action.payload]
}
case 'DESELECT_CITY':
const currentKeywordToDelete = [...state.keywords];
const indexOfKeyword = currentKeywordToDelete.findIndex(function(keyword){
return keyword === action.payload
});
return{
keywords:[...state.keywords.slice(0,indexOfKeyword), ...currentKeywordToDelete.slice(indexOfKeyword+1) ]
}
default:
return state;
}
}
COMBINEREDUCER:
import {combineReducers} from 'redux';
import {createSelector} from 'reselect';
import {Listings} from './listingsReducer';
import {filterCity} from './select-deselect-city';
import {filterCategory} from './select-deselect-category';
const reducer = combineReducers({
jobs: Listings,
keywords: filterCity,
category: filterCategory
});
export const selectJobList = (state) => state.jobs.jobs;
export const selectCities = (state) => state.keywords.keywords;
//export const selectCategories = (state) => state.categories.categories;
export const selectFilteredJobs = createSelector(
selectJobList,selectCities,
(jobs,cities) => {
if(cities && cities.length > 0){
const filteredData = Object.keys(jobs)
.filter(key => cities.includes(jobs[key].city))
.reduce((obj, key) => {
return [...obj, {...jobs[key]}]
}, []);
//console.log(filteredData);
return filteredData;
}else{
return jobs
}
}
)
export default reducer;
JSON DATA:
{
"jobs": [
{
"id": 1,
"title": "Financial Analyst II",
"description": "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. .",
"category": "Sales",
"dept": "Finance",
"date": "07/11/2018",
"experience": ["Bachelors Degree","Five years of sales experience"],
"city": "Dallas",
"state":"Texas",
"salary": "10,000"
},
{
"id": 2,
"title": "Application Support Speacialist",
"description": "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.",
"category": "Information Technology",
"dept": "Accounting",
"date": "07/10/2018",
"experience": ["Bachelors Degree","Two years of sales experience"],
"city": "Dallas",
"state":"Texas",
"salary": "20,000"
}
}

Resources