First of all: Rotation is basically useless for scripts, don't bother using it.
Instead, always use CFrame to control the orientation of parts.
Pretend there's a hinge part
Imagine you have a part representing your hinge.
As the door swings, the hinge turns around its center -- which is easy to accomplish. Visualization as GIF.
If we have a hinge, to turn it around its center we can just multiply by a "rotation CFrame" which we make with CFrame.Angles.
hinge.CFrame = hinge.CFrame * CFrame.Angles(0, 1, 0)
Note that CFrame.Angles uses radians, not degrees. 1 radian is a bit less than 57 degrees; this is so that there are 2pi radians in a full circle (equivalently there are 360 degrees in a circle).
However, we want the door to move with the hinge. To do that, we can measure the placement of the door relative to the hinge, and then move it relative to the new orientation of the hinge.
You can get how b is relative to a using relative = a.CFrame:toObjectSpace(b.CFrame). If you want to place b relative to a, you can use b.CFrame = a.CFrame:toWorldSpace(relative).
local relative = hinge.CFrame:toObjectSpace(door.CFrame)
hinge.CFrame = hinge.CFrame * CFrame.Angles(0, 1, 0)
door.CFrame = hinge.CFrame:toWorldSpace(relative)
Removing the hinge
You don't necessarily want a hinge part for every door, so ideally that wouldn't be there.
The trick is that you can find the hinge's CFrame relative to the door.
If your door is 5 x 7 x 1 (like the one is in my GIF) you can locate the left edge of the door by multiplying by a translation CFrame created with CFrame.new. The center of the door is (0, 0, 0), so the left edge would be (-2.5, 0, 0):
local hingeBefore = door.CFrame * CFrame.new(-2.5, 0, 0)
local relative = hingeBefore:toObjectSpace(door.CFrame)
local hingeAfter = hingeBefore * CFrame.Angles(0, 1, 0)
door.CFrame = hingeAfter:toWorldSpace(relative)
Animating
We can wrap up this into a nice function like this:
function swingDoor(angle)
local hingeBefore = door.CFrame * CFrame.new(-2.5, 0, 0)
local relative = hingeBefore:toObjectSpace(door.CFrame)
local hingeAfter = hingeBefore * CFrame.Angles(0, math.rad(angle), 0)
door.CFrame = hingeAfter:toWorldSpace(relative)
end
And then just call this function repeatedly to animate it opening/closing:
while true do
wait(1)
for i = 1, 100 do
swingDoor(1)
wait()
end
wait(1)
for i = 1, 100 do
swingDoor(-1)
wait()
end
end