I have auto-increment ids as primary is all of my db tables, like users, orders etc. I do not want to expose these ids to end users, as they may iterate over IDs can get access to user details. Instead I want to use a 2-way maths function such that I can obfuscate and de-obfuscate an id without storing a DB mapping.
function obfuscate(id)
{
constSeed = 1203793
return (id*constSeed)
}
function deobfuscate(bigid)
{
constSeed = 1203793
return (bigid/constSeed)
}
I can even run the bigid through a base36 converter, to get a smaller alphanumeric id, publicly exposable.
Are issues with this approach? Any other suggestions?
If you don't want them with access to the ID's maybe use them only in $_SESSION variables or something along those lines.
If the data is visible to the end user, even if you hash or encrypt the data,
it will not be safe.
Related
I have a feed table that contains id, body, created_at fields. When I send Post() on postman after Delete() method the id for the feed table auto_increments as if a record has not been deleted. I am unsure how to rectify this, I am using MySql database, nestjs and TypeORM for the backend.
feed controller.ts
#Controller("feed")
export class FeedController {
constructor(private feedService: FeedService) {}
#Post()
createNewPost(#Body() feedPost: HomeFeedDto): Observable<HomeFeedDto> {
return this.feedService.createPost(feedPost);
}
#Get()
allPosts(): Observable<HomeFeedDto[]> {
return this.feedService.getAllPosts();
}
//api delete method
#Delete(":id")
// delete home feed post by id
deleteFeedPost(#Param("id") id: number): Observable<DeleteResult> {
return this.feedService.deletePost(id);
}
}
This is just the way that auto incrementing columns work in a database. Once a record has been created that uses a particular id value it can never be used again, even if the record that owned it was deleted.
What would you expect to happen in the case where there were many records? If the current incrementing id was 1000 and then you deleted the record with id = 1 would you expect that the next time you inserted a record it would be given id = 1 again instead of id = 1001?
There are lots of practical reasons why re-using a previously issued id would be very bad for business logic especially if anyone who is a consumer of your API has a cached version of the old record.
If you really want to achieve this behavior you would have to look at writing custom functions either inside of the database or your API which check to see if any ids are missing from sequence and then manually assign your own IDs instead of letting the database do it. I would highly recommend you don't do this though as the behavior you're seeing is designed like that for a reason.
My models have both id and counter attributes. The id is a UUID, and the counter is an integer which is auto-incremented by the database.
Both are unique however I rely on id as the primary key. The counter is just a human-friendly name that I sometimes display to the user.
Immediately before an object is created a listener gives it a UUID. This works fine.
When the record is saved, MySQL increments the counter field. This works fine except that the copy of the object which I have in memory does not have the counter value. I can reload the object to find out what its counter is, but that would require another database query.
Is there a way to find the value of the counter without a specific database query? For example, is it returned as part of the response from the database when a record is created?
Few things:
Use create(array $attributes) and you'll get exactly what you want. For this having right, you have to ensure that $fillable array consists all attributes' names passed to create method.
You should use Observer on model instead of listener (most likely creating method).
Personal preference using Eloquent is that you should use id for id (increment field) and forget custom settings between models because by default it is what relations expect and so on
public function secondModels()
{
return $this->hasMany(SecondModel::class);
}
is pretty much no brainer. But for having this working best way would be (also following recommendations of this guy) FirstModel::id, SecondModel::id, SecondModel::first_model_id; first_models, second_models as table names. Avoiding and/or skipping this kind of unification is lot of custom job afterward. I don't say it can't be done but it is lot of non-first-time-successful work done.
Also, if you want visitor to get something other than id field name, you can make computed field with accessor:
/**
* Get the user's counter.
*
* #return string
*/
public function getCounterAttribute(): string
{
return (string)$this->id;
}
Which you call then with $user->counter.
Also personal preference of mine is to have most possible descriptive variable names so uuid field of mine would be something like
$table->uuid('uuid4');
This is some good and easy to make practice of Eloquent use.
Saying all this let me just to say that create() and save() will return created object from database while insert() shall not do it.
How to select a single row on october cms?
How can a simple thing be so complicated here?
I thought it would be something to help us and not to disturb something that is as simple as
SELECT * FROM `engegraph_forms_membros`
Here it's like fighting against demons without a bible, oh god why?
Why make the query difficult for newbie?
I understand you don't speak English natively but you should watch every single one of these videos.
Does the record belong to a model in a plugin? Here are the docs on how to work with models.
You make a plugin, set the database which creates models, and then make components to be ran in your CMS Pages.
In a component.php file you can have something like this: Here I am calling the model class Agreements with use Author\Plugin\Models\Agreements;. This allows me to run a function/method to retrieve all agreements or one agreements using laravel's eloquent collection services.
Lets say we have the ID of a record. Well we can either call on the Agreements model with ::find or with ::where. You will noticed I have two functions that essentially do the same thing. ::find uses the primary key of the models (in my case the id) and will return a singular record. *Note that find can take an array and return a collection of records; like ::where. Using ::where we are going to look for the ID. *Note ::where always returns a collection which is why I have included ->first().
<?php namespace Author\Plugin\Components;
use Session;
use Input;
use Crypt;
use Db;
use Redirect;
use Illuminate\Contracts\Encryption\DecryptException;
use October\Rain\Support\Collection;
use Author\Plugin\Models\Agreements;
class GetAgreement extends \Cms\Classes\ComponentBase
{
public function componentDetails()
{
return [
'name' => 'Get one agreement',
'description' => 'Get an agreement to change or delete it'
];
}
public function onRun() {
$this->page['agreement'] = $this->getWithFindAgreement;
}
public function getWithFindAgreement() {
$id = 1;
$agreement = Agreements::find($id);
return $agreement;
}
public function getWithWhereAgreement() {
$id = 1;
$agreement = Agreements::where($id)->first();
return $agreement;
}
}
If for some reason you aren't working with models, here are the docs to work with Databases. You will have to register the use Db; facade.
Here call the table you want and use ::where to query it. *Note the use of ->first() again.
$users = Db::table('users')->get();
$user = $users->where('id', 1)->first();
There are two simple ways to select a single row:
This will give you the'first' record in the selected recordset.
SELECT top 1 * FROM `engegraph_forms_membros`
This will select all the records that meet the predicate requirement that the value of <columnname> is equal to <value>
SELECT * FROM `engegraph_forms_membros` where <columnname>=<value>
If you select a record where multiple values meet that requirement, then you can (randomly) pick one by combining the solutions...
SELECT top 1 * FROM `engegraph_forms_membros` where <columnname>=<value>
But be aware that without an ORDER BY clause, the underlying data is unordered and prone to change uncontrollably, which is why most people (including your boss) will find the use of 'Top' to be improper for real use.
I am making a simple REST API in front of a NoSQL database that stores records as documents similar to JSON (but not exactly the same). Each record has some fields, including id for the database, and also including some derived fields, like dateCreated.
Any time I GET anything, I want to return the objects with all the fields.
// GET /users returns an array of these in JSON
// [{id:"xxx", name:"Bobby", dateCreated:"YYYY-MM-DD"]
data User = User { id :: String, name :: String, dateCreated :: XXX }
But any time I POST or PUT anything, they client should send an object with the id field and any derived fields missing. The database is responsible to create the id when I save it, and I would create some derived fields
// POST /users would need you to post only the name.
// {name:"Henry"}
data PartialUser = PartialUser { name :: String }
If resource represents objects of type User, what should I call the thing client is sending to me? Would you make all the derived fields Maybe values? Or would you create a second object called PostedUser or something?
It can be many things:
a request body
the representation of the intended resource state of the client
a command DTO which you can send to the domain logic in order to process it by CQRS
I would make it CreateUser command, but if you don't want to use CQRS and DDD, then you would probably call it as PartialUserRepresentation, or you don't create a data structure, just use the properties to create a new User entity. Ofc. if you use entities.
So I would say it depends on the architecture of your system.
I wonder if there is any way to declare a virtual field in CakePHP to do the following:
We have to replace a user's status with a symbol and append to it the user's nickname. For example, if a user is an admin, we want to display: #barth, for a regular user ~barth.
I already wrote an afterFind() callback to perform this task, but it fails using the containable behavior.
Either is there another way to implement it, or we can create a virtual field. The latter solution would be very elegant, but after googling I cannot find any way to use MySQL syntax to replace one string with another.
Ideas?
Virtual fields are very easy to use in Cake. You can use any regular MySQL function in their declaration to achieve this type of thing.
You'll first need to determine the SQL command to achieve what you want, I'd suggest using the CONCAT() function:
-- Return an # concatenated onto the username
CONCAT('#', yourfield)
Then add this as a virtual field in your model:
class YourModel extends AppModel {
public $virtualFields = array(
'yourVirtualField' => 'CONCAT("#", yourfield)'
);
}
Now, when you query this model you should be able to access it like this:
$example = $this->YourModel->find('first');
echo $example['YourModel']['yourVirtualField']; // #yourfield
Edit
Since your update, you've got the values you want to concatenate together in another model as virtual fields already. CakePHP doesn't allow you to use associated models' virtual fields when creating a new virtual field, but you can do a subselect query to manually get this data. Here's an SQL Fiddle example.